summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/exam/exam.cls
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /macros/latex/contrib/exam/exam.cls
Initial commit
Diffstat (limited to 'macros/latex/contrib/exam/exam.cls')
-rw-r--r--macros/latex/contrib/exam/exam.cls8306
1 files changed, 8306 insertions, 0 deletions
diff --git a/macros/latex/contrib/exam/exam.cls b/macros/latex/contrib/exam/exam.cls
new file mode 100644
index 0000000000..cc587b3d65
--- /dev/null
+++ b/macros/latex/contrib/exam/exam.cls
@@ -0,0 +1,8306 @@
+% exam.cls
+%
+% A LaTeX2e document class for preparing exams.
+
+%% exam.cls
+%% Copyright (c) 1994, 1997, 2000, 2004, 2008, 2011,
+%% 2015, 2017 Philip S. Hirschhorn
+%
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3
+% of this license or (at your option) any later version.
+% The latest version of this license is in
+% http://www.latex-project.org/lppl.txt
+% and version 1.3 or later is part of all distributions of LaTeX
+% version 2003/12/01 or later.
+%
+% This work has the LPPL maintenance status "author-maintained".
+%
+% This work consists of the files exam.cls and examdoc.tex.
+
+
+% The user's guide for exam.cls is in the file examdoc.tex.
+
+
+%%% Philip Hirschhorn
+%%% Department of Mathematics
+%%% Wellesley College
+%%% Wellesley, MA 02481
+%%% psh@math.mit.edu
+
+% The newest version of this documentclass should always be available
+% from my web page: http://www-math.mit.edu/~psh/
+
+
+\def\fileversion{2.603}
+\def\filedate{2017/12/17}
+%---------------------------------------------------------------------
+%---------------------------------------------------------------------
+%
+% If there's some feature that you'd like that this file doesn't
+% provide, tell me about it.
+%
+%
+%
+%
+%
+% Thanks to:
+%
+% Piet van Oostrum, from whose excellent ``fancyheadings.sty'' we
+% shamelessly stole most of the code for setting the headers and
+% footers.
+%
+% Mate Wierdl <mw@wierdlmpc.msci.memphis.edu>, who contributed the
+% code so that if the number of points is ``1'', then the default
+% value of \pointname will print ``1 point'' instead of ``1 points''.
+%
+% Tom Brikowski <brikowi@utdallas.edu>, who contributed the code for
+% making the number of points and number of questions available as
+% macros (as well as the idea of putting the number of points in a
+% box, instead of in parentheses). (I changed his code to make this
+% all optional, so if there are errors there, it's my fault and not
+% his.)
+%
+% Ottmar Beucher <beucher@fh-karlsruhe.de>, Dan Drake
+% <drake@math.umn.edu>, and Justus Piater <Justus.Piater@ULg.ac.be> who
+% contributed ideas and code for the \pointsofquestion and \gradetable
+% commands for printing a Grading Table. (I changed all the code to
+% make this compatible with hyperref.sty, so if there are errors there,
+% it's my fault and not theirs.)
+%
+% Justus Piater <Justus.Piater@ULg.ac.be>, who contributed the code for
+% the solution environment. (I changed his code to allow page breaks
+% inside solutions so, once again, if it's buggy, it's my fault.)
+%
+% Donald Arseneau <asnd@triumf.ca>, who created the excellent
+% ``framed.sty'' and generously allowed me to include basically the
+% whole thing in exam.cls, making the few changes needed for it to
+% work well with question environments:
+% framed.sty v 0.8a 21-Jul-2003
+% Copyright (C) 1992-2003 by Donald Arseneau
+% These macros may be freely transmitted, reproduced, or modified
+% provided that this notice is left intact.
+%
+%--------------------------------------------------------------------
+%--------------------------------------------------------------------
+% Changelog since version 2.4:
+
+%--------------------------------------------------------------------
+% Version 2.603, 2017/12/15
+
+% No longer betatest.
+
+%--------------------------------------------------------------------
+% Version 2.602$\beta$, 2017/12/15
+
+% We changed the code for multicolumn grade and point tables to remove
+% the incompatibility with colortbl.sty and other packages that load
+% colortbl.sty (e.g., xcolor.sty with the "table" option).
+
+%--------------------------------------------------------------------
+% Version 2.601$\beta$, 2017/09/22
+
+% We changed command and environment names in the code from framed.sty
+% that's included (slightly modified) in exam.cls so that the user can
+% say \usepackage{framed} without creating conflicts. This also allows
+% the user to use packages, such as minted.sty, that load framed.sty
+
+%--------------------------------------------------------------------
+% Version 2.6, 2017/09/19
+
+% No longer betatest.
+
+%--------------------------------------------------------------------
+% Version 2.510$\beta$, 2016/10/11
+
+% Bugfix: We changed \@setheadheight and \@setfootheight to fix a bug
+% that was introduced by the bugfix in version 2.306beta, 2009/03/28:
+% If the second page has a different \textheight (because of a change
+% in either headheight or footheight between pages 1 and 2), then page
+% 2 would use the \textheight of page 1. Pages 3 and beyond would get
+% the correct \textheight. The original version of this set \@colroom
+% and \vsize to the new \textheight, but that had a bug in that if a
+% float appeared at the top of a page, there would be no notice taken
+% of the space lost to the float, and so the text would overrun the
+% bottom of the page. The bugfix in version 2.306beta eliminated the
+% changes to \@colht and \vsize. In this bugfix, we adjust \@colroom,
+% \@colht, and \vsize in the same way that we adjust \textheight.
+
+%--------------------------------------------------------------------
+% Version 2.509$\beta$, 2016/09/12
+
+% Multicolumn grade and point tables, and a new syntax for multirow
+% grade and point tables (which were introduced in version
+% 2.508beta).
+
+% Multicolumn tables are vertically oriented, while multirow tables
+% are horizontally oriented, and so they do not take the optional
+% argument choosing between horizontal and vertical. They all take
+% one required argument specifying the number of columns (for
+% multicolumn) or the number of rows (for multirow).
+
+% The tables can be:
+
+% grade tables or point tables,
+
+% plain, bonus, or combined,
+
+% indexed by questions or by pages,
+
+% complete or partial.
+
+% As usual, if you omit the optional argument that chooses between
+% questions and pages, you get questions.
+
+% The new commands are:
+
+% \def\multirowgradetable{numrows}[questions or pages]
+% \def\multirowpointtable{numrows}[questions or pages]
+% \def\multirowbonusgradetable{numrows}[questions or pages]
+% \def\multirowbonuspointtable{numrows}[questions or pages]
+% \def\multirowcombinedgradetable{numrows}[questions or pages]
+% \def\multirowcombinedpointtable{numrows}[questions or pages]
+
+% \def\multirowpartialgradetable{numrows}{rangename}[questions or pages]
+% \def\multirowpartialpointtable{numrows}{rangename}[questions or pages]
+% \def\multirowpartialbonusgradetable{numrows}{rangename}[questions or pages]
+% \def\multirowpartialbonuspointtable{numrows}{rangename}[questions or pages]
+% \def\multirowpartialcombinedgradetable{numrows}{rangename}[questions or pages]
+% \def\multirowpartialcombinedpointtable{numrows}{rangename}[questions or pages]
+
+% \def\multicolumngradetable{numcols}[questions or pages]
+% \def\multicolumnpointtable{numcols}[questions or pages]
+% \def\multicolumnbonusgradetable{numcols}[questions or pages]
+% \def\multicolumnbonuspointtable{numcols}[questions or pages]
+% \def\multicolumncombinedgradetable{numcols}[questions or pages]
+% \def\multicolumncombinedpointtable{numcols}[questions or pages]
+
+% \def\multicolumnpartialgradetable{numcols}{rangename}[questions or pages]
+% \def\multicolumnpartialpointtable{numcols}{rangename}[questions or pages]
+% \def\multicolumnpartialbonusgradetable{numcols}{rangename}[questions or pages]
+% \def\multicolumnpartialbonuspointtable{numcols}{rangename}[questions or pages]
+% \def\multicolumnpartialcombinedgradetable{numcols}{rangename}[questions or pages]
+% \def\multicolumnpartialcombinedpointtable{numcols}{rangename}[questions or pages]
+
+% The older grade and point table commands can still be used. For
+% example, the commands
+
+% \gradetable[h][questions]
+% \multirowgradetable{1}[questions]
+
+% are equivalent.
+
+% The distance between the rows of a multirow table and between the
+% columns of a multicolumn table is \doublerulesep, the default value
+% of which is 2.0pt. You can change that using a \setlength command,
+% as in
+
+% \setlength{\doublerulesep}{0.5in}
+
+%--------------------------------------------------------------------
+% Version 2.508$\beta$, 2016/08/06
+
+% New commands: Multirow grade and point tables.
+
+% These are all horizontally oriented tables, and so do not take the
+% optional argument choosing between horizontal and vertical. They all
+% take one required argument specifying the number of columns, which is
+% the number of columns used for the point values (including the total),
+% but not counting the column of row headings.
+
+% Note: The syntax was changed in version 2.509beta, so that you now
+% specify the number of *rows* rather than the number of *columns*! For
+% example, the first command below should now be
+
+% \multirowgradetable{numrows}[questions or pages]
+
+% The tables can be:
+
+% grade tables or point tables,
+
+% plain, bonus, or combined,
+
+% indexed by questions or by pages,
+
+% complete or partial.
+
+% The new commands are:
+
+
+% \multirowgradetable{numcols}[questions or pages]
+% \multirowpointtable{numcols}[questions or pages]
+% \multirowbonusgradetable{numcols}[questions or pages]
+% \multirowbonuspointtable{numcols}[questions or pages]
+% \multirowcombinedgradetable{numcols}[questions or pages]
+% \multirowcombinedpointtable{numcols}[questions or pages]
+
+% \multirowpartialgradetable{numcols}{rangename}[questions or pages]
+% \multirowpartialpointtable{numcols}{rangename}[questions or pages]
+% \multirowpartialbonusgradetable{numcols}{rangename}[questions or pages]
+% \multirowpartialbonuspointtable{numcols}{rangename}[questions or pages]
+% \multirowpartialcombinedgradetable{numcols}{rangename}[questions or pages]
+% \multirowpartialcombinedpointtable{numcols}{rangename}[questions or pages]
+
+%--------------------------------------------------------------------
+% Version 2.507$\beta$, 2016/07/14
+
+% New commands:
+
+% \pointstwosided
+% \pointstwosidedreversed
+
+% The first causes points to be in the right margin on odd numbered
+% pages and in the left margin on even numbered pages.
+
+% The second causes points to be in the left margin on odd numbered
+% pages and in the right margin on even numbered pages.
+
+% Also: Some minor edits (e.g., deleting the unused \thebonuspoints).
+
+%--------------------------------------------------------------------
+% Version 2.506$\beta$, 2016/05/12
+
+% Fixed an obscure bug that arose only when \CorrectChoiceEmphasis
+% used color and a \CorrectChoice (in a choices or checkboxes
+% environment) followed a \choice whose text completely filled its
+% last line, and which was not separated from the \CorrectChoice by a
+% blank line, in which case an extra (blank) line was inserted by that
+% \choice. We fixed this by adding an improvised "\leavehmode"
+% (styled after \leavevmode) to the \CorrectChoice command in both the
+% choices and checkboxes environments, which caused the text of the
+% previous \choice to be broken into lines *before* the \special
+% inserted by the \color command was added.
+
+%--------------------------------------------------------------------
+% Version 2.505$\beta$, 2016/05/10
+
+% We fixed a bug in the choices and checkboxes environments that arose
+% only when \CorrectChoiceEmphasis used color. If it did, and if the
+% text of a correct choice exactly filled a line, and if there was no
+% blank line in the latex file separating this correct choice from the
+% following choice, there would be an extra blank line inserted after
+% the correct choice. We did this by inserting \color@begingroup and
+% \color@endgroup as needed. (We're pretty sure the actual fix was
+% the \endgraf in the expansion of \color@endgroup.)
+
+%--------------------------------------------------------------------
+% Version 2.504$\beta$, 2016/05/09
+
+% We fixed a bug in the solutionbox environment that caused enumerate,
+% itemize, or description environments to have their text stick into
+% the right margin. We did this by resetting \@totalleftmargin and
+% \linewidth in the box containing the solution.
+
+%--------------------------------------------------------------------
+% Version 2.503$\beta$, 2016/03/25
+
+% New commands:
+
+% \colorfillwithlines
+% \colorfillwithdottedlines
+
+% The first causes the lines drawn by the \fillwithlines command to be
+% drawn in color. The default color is set by the command
+
+% \definecolor{FillWithLinesColor}{gray}{0.8}
+
+% and the color can be changed by giving a new \definecolor command.
+% You can return to black lines by giving the command
+
+% \nocolorfillwithlines
+
+% \colorfillwithdottedlines causes the lines drawn by the
+% \fillwithdottedlines command to be drawn in color. The default
+% color is set by the command
+
+% \definecolor{FillWithDottedLinesColor}{gray}{0.8}
+
+% and the color can be changed by giving a new \definecolor command.
+% You can return to black dotted lines by giving the command
+
+% \nocolorfillwithdottedlines
+
+%--------------------------------------------------------------------
+% Version 2.502$\beta$, 2016/03/23
+
+% The command
+
+% \colorsolutionboxes
+
+% that was created in version 2.501beta now affects not only the boxes
+% created by \solutionbox, but also by \makeemptybox, \solutionorbox,
+% and all of the boxes printed by all of the various solution
+% environments when solutions are being printed surrounded by a box.
+
+%--------------------------------------------------------------------
+% Version 2.501$\beta$, 2016/02/08
+
+% Changed the \solutionbox environment so that it works correctly
+% inside a tabular.
+
+% Also: The \solutionbox frame can now be printed in color, as long as
+% you load color.sty in the preamble.
+%
+% Usage: Say
+%
+% \usepackage{color}
+%
+% in the preamble, and then give the command
+%
+% \colorsolutionboxes
+%
+% to have the frame around a solutionbox in color. The default color
+% was created by the command
+%
+% \definecolor{SolutionBoxColor}{gray}{0.8}
+%
+% and you can change the color by giving a new \definecolor command
+% (which must be done *after* the \colorsolutionboxes command).
+%
+% To cancel color solutionbox frames and return to black, give the
+% command
+%
+% \nocolorsolutionboxes
+
+%--------------------------------------------------------------------
+% Version 2.5 2015/05/07
+
+% No longer betatest.
+
+%--------------------------------------------------------------------
+% Version 2.408$\beta$ 2013/11/17
+
+% New commands:
+%
+% \firstqinrange{whatever}
+% \lastqinrange{whatever}
+% \numqinrange{whatever}
+%
+% where ``whatever'' is the name of a grading range.
+%
+% \firstqinrange{whatever} prints the number of the first question in
+% the range.
+%
+% \lastqinrange{whatever} prints the number of the last question in the
+% range.
+%
+% \numqinrange{whatever} prints the number of questions in the range.
+%
+% We changed a couple of internal command name related to grading
+% ranges. If the user defines the range `myrange', then we now use
+
+% \range@myrange@firstp
+% \range@myrange@lastp
+% \range@myrange@firstq
+% \range@myrange@lastq
+%
+% where we used to use
+%
+% \tbl@myrange@firstp
+% \tbl@myrange@lastp
+% \tbl@myrange@firstq
+% \tbl@myrange@lastq
+
+%--------------------------------------------------------------------
+% Version 2.407$\beta$ 2012/12/19
+
+% New environment:
+%
+% solutionbox
+%
+% The solutionbox environment is different from the other solution
+% environments (solution, solutionorbox, solutionorlines,
+% solutionordottedlines, and solutionorgrid), in that
+%
+% (1) The box is always printed, whether answers are being printed
+% or not.
+%
+% (2) The argument giving the size of the box is a required
+% argument, not an optional argument, and so it should be enclosed
+% in braces, not in brackets. It can be either a length or
+% \stretch{number}.
+%
+% (3) We make no use of the TheSolution environment; the solutionbox
+% environment is completely freestanding.
+%
+% If answers are not being printed then only the box is printed, with
+% nothing in it. If answers are being printed, then the solution is
+% printed inside of the box.
+%
+% Note: It's the user's responsibility to be sure that the box is
+% large enough to hold the solution! If the solution takes up too
+% much vertical space, then it will spill out of the bottom of the
+% box, overwriting whatever follows the box.
+%
+%--------------------------------------------------------------------
+% Version 2.406$\beta$, 2012/12/16
+%
+% New command:
+%
+% \noquestionsonthispage
+%
+% This command tells the \ifcontinuation and \ifincomplete commands
+% to assume that no part of any question is on this page. This is
+% similar to the job done by the \nomorequestions command for the
+% pages that follow the end of all of the questions.
+%
+%
+% If you give the command \noquestionsonthispage on a page, then
+%
+% (1) \ifcontinuation on that page will expand to its second
+% argument,
+% (2) \ifincomplete on that page will expand to its second
+% argument, and
+% (3) an \ifincomplete on an earlier page will not assume that a
+% question from that earlier page continues onto this page.
+%
+% The way that this command affects the \ifincomplete command on
+% earlier pages is as follows: If there is a page with no questions or
+% parts or subparts or subsubparts, then the last page before that
+% with a question (or part, etc.) would normally be deemed incomplete;
+% if, however, the page with no questions (or parts, etc.) (along with
+% all adjacent pages with no questions or parts etc.) has a
+% \noquestionsonthispage command, then that last page with a question
+% (or part, etc.) will not be deemed incomplete.
+%
+% Note that if you're tempted to use this command on a page that follows
+% the end of all of the questions, then you should probably use the
+% command \nomorequestions instead.
+%
+%--------------------------------------------------------------------
+% Version 2.405$\beta$, 2012/10/21
+%
+% It is now possible to use a parts, subparts, or subsubparts
+% environment inside one of the solution environments (solution,
+% solutionorbox, solutionorlines, solutionordottedlines, or
+% solutionorgrid) without getting problems from multiply defined
+% labels or having its points (if any) counted as being actual points
+% on the exam.
+%
+% Any \part, \subpart, or \subsubpart command inside one of the
+% solution environments now writes a \PgInfo command in the .aux file
+% of the form question2@object3, but no labels and no other \PgInfo
+% commands. In addition, if there are points assigned to any of these
+% commands inside any of the solution environments, those points are
+% not added to the points of the question or the points on the page,
+% and do not affect any gradetables or pointtables.
+%
+%--------------------------------------------------------------------
+% Version 2.404$\beta$, 2012/09/03
+%
+% New command:
+%
+% \fillwithgrid{length}
+%
+% New environment:
+%
+% solutionorgrid
+%
+% These are similar to the \fillwithlines command and the
+% solutionorlines environment.
+%
+% By default, the created grids are in black. However, if you give the
+% commands
+%
+% \usepackage{color}
+% \colorgrids
+%
+% then the grids will be in color, by default a light gray. That
+% default color was defined by the command
+%
+% \definecolor{GridColor}{gray}{0.8}
+%
+% You can change the color by redefining the color GridColor, and you
+% can return to using black grids by giving the command
+%
+% \nocolorgrids
+%
+% The default grid size and grid line thickness were set by the
+% commands
+%
+% \setlength{\gridsize}{5mm}
+% \setlength{\gridlinewidth}{0.1pt}
+%
+% You can change either or both of those by giving new \setlength
+% commands. The period of the grid is \gridsize (both horizontally
+% and vertically). That is, the horizontal distance from the left
+% edge of one vertical line to the left edge of the next vertical line
+% is \gridsize, as is the vertical distance from the top edge of one
+% horizontal line to the top edge of the next horizontal line. Thus,
+% each square has outer side length equal to \gridsize+\gridlinewidth.
+%
+%--------------------------------------------------------------------
+% Version 2.403$\beta$, 2012/08/29:
+%
+% We changed the code for the command \fillin (which had been modified
+% in version 2.402beta) so that if only one optional argument is used,
+% a space following that optional argument will not be ignored. We
+% did this in such a way that the second optional argument will be
+% recognized even when spaces appear in between the optional
+% arguments.
+%
+%--------------------------------------------------------------------
+% Version 2.402$\beta$, 2012/08/21:
+%
+% We modified the command \fillin that we had created in version
+% 2.401beta. \fillin now takes two optional arguments (and no required
+% arguments).
+%
+% \fillin can take two optional arguments, as in
+%
+% \fillin[Answer][Length]
+%
+% The first optional argument is the answer to be printed above the line
+% when \printanswers is in effect; the default value is empty. That
+% line is printed a distance of \answerclearance below the baseline.
+%
+% The second optional argument is the length of the line that we print;
+% the default value is \fillinlinelength. The value of
+% \fillinlinelength is set with the command
+%
+% \setlength\fillinlinelength{1in}
+%
+% and can be changed by giving a new \setlength command.
+%
+% When answers are being printed, the first optional argument is
+% printed subject to the declarations in the argument of the last
+% \CorrectChoiceEmphasis command. It is centered on the line unless
+% it is too long, in which case it extends to the right of the line.
+%
+%--------------------------------------------------------------------
+% Version 2.401$\beta$, 2012/08/20:
+%
+%
+% New command:
+%
+% \fillin[CorrectAnswer]{width}
+%
+% This is for use in fill in the blank questions. This command inserts
+% a blank line of width ``width''. If answers are being printed and if
+% the optional argument ``CorrectAnswer'' appears, then the optional
+% argument is printed subject to the declarations in the argument of the
+% last \CorrectChoiceEmphasis command, and it is printed a distance of
+% \answerclearance above the line. It is centered on the line unless it
+% is too long, in which case it extends to the right of the line.
+%
+% Note: We changed this command in version 2.401beta.
+%
+%--------------------------------------------------------------------
+%--------------------------------------------------------------------
+%--------------------------------------------------------------------
+
+\NeedsTeXFormat{LaTeX2e}
+
+\ProvidesClass{exam}[\filedate\space Version \fileversion\space by
+ Philip Hirschhorn]
+
+\RequirePackage{ifthen}
+
+\newif\ifprintanswers
+\printanswersfalse
+\DeclareOption{answers}{\printanswerstrue}
+\DeclareOption{noanswers}{\printanswersfalse}
+
+\newif\ifcancelspace
+\cancelspacefalse
+\DeclareOption{cancelspace}{\cancelspacetrue}
+\DeclareOption{nocancelspace}{\cancelspacefalse}
+
+% The following keeps track of whether the user has requested that we
+% add up the points on the exam. We make the default false so that
+% users who put other than numbers into the points argument of a
+% question (or part, or subpart) won't get error messages.
+% We use \if@printtotalpoints as a flag to signal that we are counting
+% points, so that we will know to print the total on the screen (and
+% in the log file). We use this separate flag so that the user can
+% use both \addpoints and \noaddpoints to count some points and not
+% others, but still have the total printed when we finish the file no
+% matter what the state of \if@addpoints.
+\newif\if@addpoints
+\newif\if@printtotalpoints
+\def\addpoints{\global\@addpointstrue\global\@printtotalpointstrue}
+\def\noaddpoints{\global\@addpointsfalse}
+\@addpointsfalse
+\@printtotalpointsfalse
+\DeclareOption{addpoints}{\addpoints}
+
+
+\DeclareOption*{%
+ \PassOptionsToClass{\CurrentOption}{article}%
+}
+\ProcessOptions\relax
+\LoadClass{article}
+
+
+
+
+% *****************
+% ** PAGE LAYOUT **
+% *****************
+
+
+% We set the parameters in terms of \paperwidth and \paperheight
+% so that the options
+
+% a4paper
+% a5paper
+% b5paper
+% letterpaper
+% legalpaper
+% executivepaper
+% landscape
+
+% will all work:
+
+\setlength{\textwidth}{\paperwidth}
+\addtolength{\textwidth}{-2in}
+\setlength{\oddsidemargin}{0pt}
+\setlength{\evensidemargin}{0pt}
+
+\setlength{\headheight}{15pt}
+\setlength{\headsep}{15pt}
+\setlength{\topmargin}{0in}
+\addtolength{\topmargin}{-\headheight}
+\addtolength{\topmargin}{-\headsep}
+\setlength{\footskip}{29pt}
+\setlength{\textheight}{\paperheight}
+\addtolength{\textheight}{-2.2in}
+
+\setlength{\marginparwidth}{.5in}
+\setlength{\marginparsep}{5pt}
+
+%--------------------------------------------------------------------
+
+% ****************
+% ** EXTRAWIDTH **
+% ****************
+
+\newlength\@extrawidth
+
+% \@rightmargin is needed for \pointsinrightmargin and
+% \pointsdroppedatright, so that we can right justify the points:
+\newlength\@rightmargin
+\setlength{\@rightmargin}{1in}
+
+% We put the argument of \extrawidth into a length so that it will
+% work correctly even if it's negative:
+
+\def\extrawidth#1{%
+ \@extrawidth=#1
+ \advance \textwidth by \@extrawidth
+ \divide\@extrawidth by 2
+ \advance\oddsidemargin by -\@extrawidth
+ \advance\evensidemargin by -\@extrawidth
+ % Bug fix, 13 April 2004:
+ %\advance\@rightmargin by \@extrawidth
+ \advance\@rightmargin by -\@extrawidth
+}
+
+%--------------------------------------------------------------------
+%--------------------------------------------------------------------
+% Making room for large headers and footers
+
+% The following are used to save the effect of any changes to
+% \topmargin and \textheight caused by \extraheadheight or
+% \extrafootheight commands. They hold the values currently in effect.
+% We put them into lengths so that it will work correctly even if the
+% argument is negative:
+
+\newlength\@extrahead
+\newlength\@extrafoot
+\setlength{\@extrahead}{0in}
+\setlength{\@extrafoot}{0in}
+
+% The following are used to hold the requested values for extrahead and
+% extrafoot, first page and all pages after the first, and then the
+% similar things requested for the cover pages:
+\newlength\run@exhd
+\newlength\fp@exhd
+\newlength\run@exft
+\newlength\fp@exft
+\newlength\covrun@exhd
+\newlength\covfp@exhd
+\newlength\covrun@exft
+\newlength\covfp@exft
+
+\setlength{\run@exhd}{0in}
+\setlength{\fp@exhd}{0in}
+\setlength{\run@exft}{0in}
+\setlength{\fp@exft}{0in}
+\setlength{\covrun@exhd}{0in}
+\setlength{\covfp@exhd}{0in}
+\setlength{\covrun@exft}{0in}
+\setlength{\covfp@exft}{0in}
+
+\newcommand*\adj@hdht@ftht{%
+ \if@coverpages
+ \ifnum\value{page}=1\relax
+ \@setheadheight{\covfp@exhd}%
+ \@setfootheight{\covfp@exft}%
+ \else
+ \@setheadheight{\covrun@exhd}%
+ \@setfootheight{\covrun@exft}%
+ \fi
+ \else
+ \ifnum\value{page}=1\relax
+ \@setheadheight{\fp@exhd}%
+ \@setfootheight{\fp@exft}%
+ \else
+ \@setheadheight{\run@exhd}%
+ \@setfootheight{\run@exft}%
+ \fi
+ \fi
+}
+
+\newcommand*\extraheadheight{%
+ \@ifnextchar[{\@xtrahd}{\@ytrahd}%
+}
+
+\def\@xtrahd[#1]#2{%
+ \setlength{\fp@exhd}{#1}%
+ \setlength{\run@exhd}{#2}%
+ \adj@hdht@ftht
+}
+
+\def\@ytrahd#1{%
+ \setlength{\fp@exhd}{#1}%
+ \setlength{\run@exhd}{#1}%
+ \adj@hdht@ftht
+}
+
+\newcommand*\extrafootheight{%
+ \@ifnextchar[{\@xtraft}{\@ytraft}%
+}
+
+\def\@xtraft[#1]#2{%
+ \setlength{\fp@exft}{#1}%
+ \setlength{\run@exft}{#2}%
+ \adj@hdht@ftht
+}
+
+\def\@ytraft#1{%
+ \setlength{\fp@exft}{#1}%
+ \setlength{\run@exft}{#1}%
+ \adj@hdht@ftht
+}
+
+\newcommand*\coverextraheadheight{%
+ \@ifnextchar[{\cov@xtrahd}{\cov@ytrahd}%
+}
+
+\def\cov@xtrahd[#1]#2{%
+ \setlength{\covfp@exhd}{#1}%
+ \setlength{\covrun@exhd}{#2}%
+ \adj@hdht@ftht
+}
+
+\def\cov@ytrahd#1{%
+ \setlength{\covfp@exhd}{#1}%
+ \setlength{\covrun@exhd}{#1}%
+ \adj@hdht@ftht
+}
+
+\newcommand*\coverextrafootheight{%
+ \@ifnextchar[{\cov@xtraft}{\cov@ytraft}%
+}
+
+\def\cov@xtraft[#1]#2{%
+ \setlength{\covfp@exft}{#1}%
+ \setlength{\covrun@exft}{#2}%
+ \adj@hdht@ftht
+}
+
+\def\cov@ytraft#1{%
+ \setlength{\covfp@exft}{#1}%
+ \setlength{\covrun@exft}{#1}%
+ \adj@hdht@ftht
+}
+
+\def\@appendoutput#1{%
+ \output=\expandafter{\the\output #1}%
+}
+
+\@appendoutput{\adj@hdht@ftht}
+
+%--------------------------------------------------------------------
+% \@setheadheight and \@setfootheight:
+
+\def\@setheadheight#1{%
+ \begingroup % Avoid trouble from using \@temp and \@spaces
+ % Reset the effect of the most recent change:
+ \global\advance\topmargin by -\@extrahead
+ \global\advance\textheight by \@extrahead
+ % Bugfix, Version 2.510beta, 2016/10/11:
+ \global\advance\@colroom by \@extrahead
+ \global\advance\@colht by \@extrahead
+ \global\advance\vsize by \@extrahead
+ %
+ % Save the newly set value:
+ \def\@temp{#1}
+ \def\@spaces{ }
+ \ifx\@temp\@empty
+ \global\@extrahead=0in
+ \else
+ \ifx\@temp\@spaces
+ \global\@extrahead=0in
+ \else
+ \global\@extrahead=#1
+ \fi
+ \fi
+ % Set the new values:
+ \global\advance\topmargin by \@extrahead
+ \global\advance\textheight by -\@extrahead
+ % Bugfix, Version 2.510beta, 2016/10/11:
+ \global\advance\@colroom by -\@extrahead
+ \global\advance\@colht by -\@extrahead
+ \global\advance\vsize by -\@extrahead
+ %
+ % Bugfix, Version 2.510beta, 2016/10/11:
+ % We're fixing a bug that was introduced by the bugfix in version
+ % 2.306beta, 2009/03/28: If the second page has a different
+ % \textheight (because of a change in either headheight or
+ % footheight between pages 1 and 2), then page 2 would use the
+ % \textheight of page 1. Pages 3 and beyond would get the correct
+ % \textheight.
+ % The original version of this set \@colroom and \vsize to the new
+ % \textheight, but that had a bug in that if a float appeared at
+ % the top of a page, there would be no notice taken of the space
+ % lost to the float, and so the text would overrun the bottom of
+ % the page.
+ % In this bugfix, we adjust \@colroom, \@colht, and \vsize in the
+ % same way that we adjust \textheight.
+ %
+ % Make it take effect RIGHT NOW!:
+ % (The following stuff isn't necessary if \@setheadheight is
+ % executed only in the preamble or as we return from the output
+ % routine, but we're leaving it in so that this will still work if
+ % we use this at some random point in the middle of composing a
+ % page).
+ % Bugfix, Version 2.306beta, 2009/03/28:
+ % We don't do this!!
+ % If the user had a figure environment that floated to the
+ % top of a page, then this would cause that page to run
+ % over the footer and off the bottom of the page, because
+ % this somehow caused a full page's worth of stuff to be
+ % placed after the figure, as if the figure wasn't taking
+ % up space on the page.
+ % We *do* need to put \@colht at the correct new value, though,
+ % apparently because \@colht is set near the end of the
+ % output routine.
+% \global\@colht=\textheight
+%
+% \global\@colroom=\textheight
+% \global\vsize=\textheight
+% \global\pagegoal=\textheight
+ \endgroup
+}% @setheadheight
+
+\def\@setfootheight#1{%
+ \begingroup % Avoid trouble from using \@temp and \@spaces
+ % Reset the effect of the most recent change:
+ \global\advance\textheight by \@extrafoot
+ % Bugfix, Version 2.510beta, 2016/10/11:
+ \global\advance\@colroom by \@extrafoot
+ \global\advance\@colht by \@extrafoot
+ \global\advance\vsize by \@extrafoot
+ %
+ % Save the newly set value:
+ \def\@temp{#1}
+ \def\@spaces{ }
+ \ifx\@temp\@empty
+ \global\@extrafoot=0in
+ \else
+ \ifx\@temp\@spaces
+ \global\@extrafoot=0in
+ \else
+ \global\@extrafoot=#1
+ \fi
+ \fi
+ % Set the new values:
+ \global\advance\textheight by -\@extrafoot
+ % Bugfix, Version 2.510beta, 2016/10/11:
+ \global\advance\@colroom by -\@extrafoot
+ \global\advance\@colht by -\@extrafoot
+ \global\advance\vsize by -\@extrafoot
+ %
+ % Bugfix, Version 2.510beta, 2016/10/11:
+ % We're fixing a bug that was introduced by the bugfix in version
+ % 2.306beta, 2009/03/28: If the second page has a different
+ % \textheight (because of a change in either headheight or
+ % footheight between pages 1 and 2), then page 2 would use the
+ % \textheight of page 1. Pages 3 and beyond would get the correct
+ % \textheight.
+ % The original version of this set \@colroom and \vsize to the new
+ % \textheight, but that had a bug in that if a float appeared at
+ % the top of a page, there would be no notice taken of the space
+ % lost to the float, and so the text would overrun the bottom of
+ % the page.
+ % In this bugfix, we adjust \@colroom, \@colht, and \vsize in the
+ % same way that we adjust \textheight.
+ %
+ % Make it take effect RIGHT NOW!:
+ % (The following stuff isn't necessary if \@setfootheight is
+ % executed only in the preamble or as we return from the output
+ % routine, but we're leaving it in so that this will still work if
+ % we use this at some random point in the middle of composing a
+ % page).
+ % Bugfix, Version 2.306beta, 2009/03/28:
+ % We don't do this!!
+ % If the user had a figure environment that floated to the
+ % top of a page, then this would cause that page to run
+ % over the footer and off the bottom of the page, because
+ % this somehow caused a full page's worth of stuff to be
+ % placed after the figure, as if the figure wasn't taking
+ % up space on the page.
+ % We *do* need to put \@colht at the correct new value, though,
+ % apparently because \@colht is set near the end of the
+ % output routine.
+% \global\@colht=\textheight
+%
+% \global\@colroom=\textheight
+% \global\vsize=\textheight
+% \global\pagegoal=\textheight
+ \endgroup
+}% @setfootheight
+
+
+%---------------------------------------------------------------------
+%
+% *************************
+% ** HEADERS AND FOOTERS **
+% *************************
+%
+% The pagestyles available are head, foot, headandfoot, and empty.
+% \pagestyle{head} prints the head, and gives an empty foot.
+% \pagestyle{foot} prints the foot, and gives an empty head.
+% \pagestyle{headandfoot} prints both the head and the foot.
+% \pagestyle{empty} gives an empty head and an empty foot.
+%
+
+% Pagestyles:
+
+\newcommand*\ps@head{%
+ \@dohead
+ \@nofoot
+}
+
+\newcommand*\ps@headandfoot{%
+ \@dohead
+ \@dofoot
+}
+
+\newcommand*\ps@foot{%
+ \@nohead
+ \@dofoot
+}
+
+% \ps@empty is already defined by article.cls, so we'll
+% say \def instead of \newcommand*:
+\def\ps@empty{%
+ \@nohead
+ \@nofoot
+}
+
+\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\relax
+ \ClassError{exam}{%
+ Coverpages cannot be used after questions have begun.\MessageBreak
+ }{%
+ All question, part, subpart, and subsubpart environments
+ \MessageBreak
+ must begin after the cover pages are complete.\MessageBreak
+ }%
+ \fi
+ \@coverpagestrue
+ \pagenumbering{roman}%
+ \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
+}
+
+\newcommand*\cover@question@error{%
+ \ClassError{exam}{%
+ No questions are allowed in the cover pages.\MessageBreak
+ }{%
+ All question, part, subpart, and subsubpart environments
+ \MessageBreak
+ must begin after the cover pages are complete.\MessageBreak
+ }%
+}
+
+\newcommand*\@dohead{%
+ \def\@oddhead{%
+ \if@coverpages
+ \ifnum\value{page}=1\relax
+ \cov@fullhead
+ \else
+ \covrun@fullhead
+ \fi
+ \else
+ \ifnum\value{page}=1\relax
+ \@fullhead
+ \else
+ \run@fullhead
+ \fi
+ \fi
+ }% @oddhead
+ \let\@evenhead=\@oddhead
+}
+
+\newcommand*\@dofoot{%
+ \def\@oddfoot{%
+ \if@coverpages
+ \ifnum\value{page}=1\relax
+ \cov@fullfoot
+ \else
+ \covrun@fullfoot
+ \fi
+ \else
+ \ifnum\value{page}=1\relax
+ \@fullfoot
+ \else
+ \run@fullfoot
+ \fi
+ \fi
+ }% @oddfoot
+ \let\@evenfoot=\@oddfoot
+}
+
+\newcommand*\@nohead{%
+ \def\@oddhead{}%
+ \let\@evenhead=\@oddhead
+}
+
+\newcommand*\@nofoot{%
+ \def\@oddfoot{}%
+ \let\@evenfoot=\@oddfoot
+}
+
+%--------------------------------------------------------------------
+% \@fullhead, \run@fullhead, \@fullfoot, and \run@fullfoot:
+
+\newcommand*\@fullhead{%
+ \vbox to \headheight{%
+ \vss
+ \hbox to \textwidth{%
+ \normalfont\rlap{\parbox[b]{\textwidth}{\raggedright\@lhead\strut}}%
+ \hss\parbox[b]{\textwidth}{\centering\@chead\strut}\hss
+ \llap{\parbox[b]{\textwidth}{\raggedleft\@rhead\strut}}%
+ }% hbox
+ \if@headrule
+ \hrule
+ \else
+ % an invisible hrule, to keep positioning constant:
+ \hrule width 0pt
+ \fi
+ }% vbox
+}
+
+\newcommand*\run@fullhead{%
+ \vbox to \headheight{%
+ \vss
+ \hbox to \textwidth{%
+ \normalfont\rlap{\parbox[b]{\textwidth}{\raggedright\run@lhead\strut}}%
+ \hss\parbox[b]{\textwidth}{\centering\run@chead\strut}\hss
+ \llap{\parbox[b]{\textwidth}{\raggedleft\run@rhead\strut}}%
+ }% hbox
+ \ifrun@headrule
+ \hrule
+ \else
+ % an invisible hrule, to keep positioning constant:
+ \hrule width 0pt
+ \fi
+ }% vbox
+}
+
+% We arrange it so that the very top of first line of text in the
+% foot is at a fixed position on the page, whether or not there's
+% a footrule:
+
+\newcommand*\@fullfoot{%
+ \vbox to 0pt{%
+ \if@footrule
+ \hrule
+ \else
+ % an invisible hrule, to keep positioning constant:
+ \hrule width 0pt
+ \fi
+ \vskip 3pt
+ \hbox to \textwidth{%
+ \normalfont\rlap{\parbox[t]{\textwidth}{\raggedright\@lfoot}}%
+ \hss\parbox[t]{\textwidth}{\centering\@cfoot}\hss
+ \llap{\parbox[t]{\textwidth}{\raggedleft\@rfoot}}%
+ }% hbox
+ \vss
+ }% vbox
+}
+
+\newcommand*\run@fullfoot{%
+ \vbox to 0pt{%
+ \ifrun@footrule
+ \hrule
+ \else
+ % an invisible hrule, to keep positioning constant:
+ \hrule width 0pt
+ \fi
+ \vskip 3pt
+ \hbox to \textwidth{%
+ \normalfont\rlap{\parbox[t]{\textwidth}{\raggedright\run@lfoot}}%
+ \hss\parbox[t]{\textwidth}{\centering\run@cfoot}\hss
+ \llap{\parbox[t]{\textwidth}{\raggedleft\run@rfoot}}%
+ }% hbox
+ \vss
+ }% vbox
+}
+
+%--------------------------------------------------------------------
+% \cov@fullhead, \covrun@fullhead, \cov@fullfoot, and
+% \covrun@fullfoot:
+
+\newcommand*\cov@fullhead{%
+ \vbox to \headheight{%
+ \vss
+ \hbox to \textwidth{%
+ \normalfont\rlap{\parbox[b]{\textwidth}{\raggedright\cov@lhead\strut}}%
+ \hss\parbox[b]{\textwidth}{\centering\cov@chead\strut}\hss
+ \llap{\parbox[b]{\textwidth}{\raggedleft\cov@rhead\strut}}%
+ }% hbox
+ \ifcov@headrule
+ \hrule
+ \else
+ % an invisible hrule, to keep positioning constant:
+ \hrule width 0pt
+ \fi
+ }% vbox
+}
+
+\newcommand*\covrun@fullhead{%
+ \vbox to \headheight{%
+ \vss
+ \hbox to \textwidth{%
+ \normalfont\rlap{\parbox[b]{\textwidth}{\raggedright\covrun@lhead\strut}}%
+ \hss\parbox[b]{\textwidth}{\centering\covrun@chead\strut}\hss
+ \llap{\parbox[b]{\textwidth}{\raggedleft\covrun@rhead\strut}}%
+ }% hbox
+ \ifcovrun@headrule
+ \hrule
+ \else
+ % an invisible hrule, to keep positioning constant:
+ \hrule width 0pt
+ \fi
+ }% vbox
+}
+
+% We arrange it so that the very top of first line of text in the
+% foot is at a fixed position on the page, whether or not there's
+% a footrule:
+
+\newcommand*\cov@fullfoot{%
+ \vbox to 0pt{%
+ \ifcov@footrule
+ \hrule
+ \else
+ % an invisible hrule, to keep positioning constant:
+ \hrule width 0pt
+ \fi
+ \vskip 3pt
+ \hbox to \textwidth{%
+ \normalfont\rlap{\parbox[t]{\textwidth}{\raggedright\cov@lfoot}}%
+ \hss\parbox[t]{\textwidth}{\centering\cov@cfoot}\hss
+ \llap{\parbox[t]{\textwidth}{\raggedleft\cov@rfoot}}%
+ }% hbox
+ \vss
+ }% vbox
+}
+
+\newcommand*\covrun@fullfoot{%
+ \vbox to 0pt{%
+ \ifcovrun@footrule
+ \hrule
+ \else
+ % an invisible hrule, to keep positioning constant:
+ \hrule width 0pt
+ \fi
+ \vskip 3pt
+ \hbox to \textwidth{%
+ \normalfont\rlap{\parbox[t]{\textwidth}{\raggedright\covrun@lfoot}}%
+ \hss\parbox[t]{\textwidth}{\centering\covrun@cfoot}\hss
+ \llap{\parbox[t]{\textwidth}{\raggedleft\covrun@rfoot}}%
+ }% hbox
+ \vss
+ }% vbox
+}
+
+%--------------------------------------------------------------------
+%--------------------------------------------------------------------
+%
+% ********************************************
+% ** COMMANDS TO DEFINE HEADERS AND FOOTERS **
+% ********************************************
+%
+% \lhead[#1]{#2} sets the first page left head to #1, and the
+% running left head to #2
+%
+% \lhead{#1} sets both the first page left head and the running
+% left head to #1
+%
+% \chead, \rhead, \lfoot, \cfoot, and \rfoot work similarly.
+%
+%
+% \@lhead is the left head for Page 1
+% \run@lhead is the running left head
+% (i.e., for all pages other than the first)
+%
+% \@chead is the center head for Page 1
+% \run@chead is the running center head
+% (i.e., for all pages other than the first)
+%
+% etc.
+%
+% Alternative commands are:
+% \firstpageheader{LEFT}{CENTER}{RIGHT}
+% \runningheader{LEFT}{CENTER}{RIGHT}
+% or
+% \header{LEFT}{CENTER}{RIGHT}
+% which is equivalent to the two commands
+% \firstpageheader{LEFT}{CENTER}{RIGHT}
+% \runningheader{LEFT}{CENTER}{RIGHT}
+%
+% Alternative commands are:
+% \firstpagefooter{LEFT}{CENTER}{RIGHT}
+% \runningfoother{LEFT}{CENTER}{RIGHT}
+% or
+% \footer{LEFT}{CENTER}{RIGHT}
+% which is equivalent to the two commands
+% \firstpagefooter{LEFT}{CENTER}{RIGHT}
+% \runningfoother{LEFT}{CENTER}{RIGHT}
+
+\def\firstpageheader#1#2#3{%
+ \def\@lhead{#1}%
+ \def\@chead{#2}%
+ \def\@rhead{#3}%
+}
+
+\def\runningheader#1#2#3{%
+ \def\run@lhead{#1}%
+ \def\run@chead{#2}%
+ \def\run@rhead{#3}%
+}
+
+\def\header#1#2#3{%
+ \firstpageheader{#1}{#2}{#3}%
+ \runningheader{#1}{#2}{#3}%
+}
+
+\def\firstpagefooter#1#2#3{%
+ \def\@lfoot{#1}%
+ \def\@cfoot{#2}%
+ \def\@rfoot{#3}%
+}
+
+\def\runningfooter#1#2#3{%
+ \def\run@lfoot{#1}%
+ \def\run@cfoot{#2}%
+ \def\run@rfoot{#3}%
+}
+
+\def\footer#1#2#3{%
+ \firstpagefooter{#1}{#2}{#3}%
+ \runningfooter{#1}{#2}{#3}%
+}
+
+\def\lhead{\@ifnextchar[{\@xlhead}{\@ylhead}}
+\def\@xlhead[#1]#2{\def\@lhead{#1}\def\run@lhead{#2}}
+\def\@ylhead#1{\def\run@lhead{#1}\def\@lhead{#1}}
+
+\def\chead{\@ifnextchar[{\@xchead}{\@ychead}}
+\def\@xchead[#1]#2{\def\@chead{#1}\def\run@chead{#2}}
+\def\@ychead#1{\def\run@chead{#1}\def\@chead{#1}}
+
+\def\rhead{\@ifnextchar[{\@xrhead}{\@yrhead}}
+\def\@xrhead[#1]#2{\def\@rhead{#1}\def\run@rhead{#2}}
+\def\@yrhead#1{\def\run@rhead{#1}\def\@rhead{#1}}
+
+\def\lfoot{\@ifnextchar[{\@xlfoot}{\@ylfoot}}
+\def\@xlfoot[#1]#2{\def\@lfoot{#1}\def\run@lfoot{#2}}
+\def\@ylfoot#1{\def\run@lfoot{#1}\def\@lfoot{#1}}
+
+\def\cfoot{\@ifnextchar[{\@xcfoot}{\@ycfoot}}
+\def\@xcfoot[#1]#2{\def\@cfoot{#1}\def\run@cfoot{#2}}
+\def\@ycfoot#1{\def\run@cfoot{#1}\def\@cfoot{#1}}
+
+\def\rfoot{\@ifnextchar[{\@xrfoot}{\@yrfoot}}
+\def\@xrfoot[#1]#2{\def\@rfoot{#1}\def\run@rfoot{#2}}
+\def\@yrfoot#1{\def\run@rfoot{#1}\def\@rfoot{#1}}
+
+
+% Initialize head and foot:
+
+\pagestyle{headandfoot}
+
+\lhead{}
+\chead{}
+\rhead{}
+\lfoot{}
+\cfoot[]{Page \thepage}
+\rfoot{}
+
+%--------------------------------------------------------------------
+% Coverpage headers and footers
+%
+% \coverlhead[#1]{#2} sets the first cover page left head to #1, and the
+% running cover left head to #2
+%
+% \coverlhead{#1} sets both the first cover page left head and the running
+% cover left head to #1
+%
+% \coverchead, \coverrhead, \coverlfoot, \covercfoot, and \coverrfoot
+% work similarly.
+%
+%
+% \cov@lhead is the left head for Page 1
+% \covrun@lhead is the running left head
+% (i.e., for all pages other than the first)
+%
+% \cov@chead is the center head for Page 1
+% \covrun@chead is the running center head
+% (i.e., for all pages other than the first)
+%
+% etc.
+%
+% Alternative commands are:
+% \coverfirstpageheader{LEFT}{CENTER}{RIGHT}
+% \coverrunningheader{LEFT}{CENTER}{RIGHT}
+% or
+% \coverheader{LEFT}{CENTER}{RIGHT}
+% which is equivalent to the two commands
+% \coverfirstpageheader{LEFT}{CENTER}{RIGHT}
+% \coverrunningheader{LEFT}{CENTER}{RIGHT}
+%
+% Alternative commands are:
+% \coverfirstpagefooter{LEFT}{CENTER}{RIGHT}
+% \coverrunningfoother{LEFT}{CENTER}{RIGHT}
+% or
+% \coverfooter{LEFT}{CENTER}{RIGHT}
+% which is equivalent to the two commands
+% \coverfirstpagefooter{LEFT}{CENTER}{RIGHT}
+% \coverrunningfoother{LEFT}{CENTER}{RIGHT}
+
+\def\coverfirstpageheader#1#2#3{%
+ \def\cov@lhead{#1}%
+ \def\cov@chead{#2}%
+ \def\cov@rhead{#3}%
+}
+
+\def\coverrunningheader#1#2#3{%
+ \def\covrun@lhead{#1}%
+ \def\covrun@chead{#2}%
+ \def\covrun@rhead{#3}%
+}
+
+\def\coverheader#1#2#3{%
+ \coverfirstpageheader{#1}{#2}{#3}%
+ \coverrunningheader{#1}{#2}{#3}%
+}
+
+\def\coverfirstpagefooter#1#2#3{%
+ \def\cov@lfoot{#1}%
+ \def\cov@cfoot{#2}%
+ \def\cov@rfoot{#3}%
+}
+
+\def\coverrunningfooter#1#2#3{%
+ \def\covrun@lfoot{#1}%
+ \def\covrun@cfoot{#2}%
+ \def\covrun@rfoot{#3}%
+}
+
+\def\coverfooter#1#2#3{%
+ \coverfirstpagefooter{#1}{#2}{#3}%
+ \coverrunningfooter{#1}{#2}{#3}%
+}
+
+\def\coverlhead{\@ifnextchar[{\cov@xlhead}{\cov@ylhead}}
+\def\cov@xlhead[#1]#2{\def\cov@lhead{#1}\def\covrun@lhead{#2}}
+\def\cov@ylhead#1{\def\covrun@lhead{#1}\def\cov@lhead{#1}}
+
+\def\coverchead{\@ifnextchar[{\cov@xchead}{\cov@ychead}}
+\def\cov@xchead[#1]#2{\def\cov@chead{#1}\def\covrun@chead{#2}}
+\def\cov@ychead#1{\def\covrun@chead{#1}\def\cov@chead{#1}}
+
+\def\coverrhead{\@ifnextchar[{\cov@xrhead}{\cov@yrhead}}
+\def\cov@xrhead[#1]#2{\def\cov@rhead{#1}\def\covrun@rhead{#2}}
+\def\cov@yrhead#1{\def\covrun@rhead{#1}\def\cov@rhead{#1}}
+
+\def\coverlfoot{\@ifnextchar[{\cov@xlfoot}{\cov@ylfoot}}
+\def\cov@xlfoot[#1]#2{\def\cov@lfoot{#1}\def\covrun@lfoot{#2}}
+\def\cov@ylfoot#1{\def\covrun@lfoot{#1}\def\cov@lfoot{#1}}
+
+\def\covercfoot{\@ifnextchar[{\cov@xcfoot}{\cov@ycfoot}}
+\def\cov@xcfoot[#1]#2{\def\cov@cfoot{#1}\def\covrun@cfoot{#2}}
+\def\cov@ycfoot#1{\def\covrun@cfoot{#1}\def\cov@cfoot{#1}}
+
+\def\coverrfoot{\@ifnextchar[{\cov@xrfoot}{\cov@yrfoot}}
+\def\cov@xrfoot[#1]#2{\def\cov@rfoot{#1}\def\covrun@rfoot{#2}}
+\def\cov@yrfoot#1{\def\covrun@rfoot{#1}\def\cov@rfoot{#1}}
+
+
+% Initialize coverpage head and foot:
+
+\coverlhead{}
+\coverchead{}
+\coverrhead{}
+\coverlfoot{}
+\covercfoot{}
+\coverrfoot{}
+
+
+
+
+%--------------------------------------------------------------------
+%--------------------------------------------------------------------
+
+% Headrules and footrules:
+
+\newif\if@headrule
+\newif\ifrun@headrule
+
+\def\firstpageheadrule{\@headruletrue}
+\def\nofirstpageheadrule{\@headrulefalse}
+
+\def\runningheadrule{\run@headruletrue}
+\def\norunningheadrule{\run@headrulefalse}
+
+\def\headrule{\@headruletrue\run@headruletrue}
+\def\noheadrule{\@headrulefalse\run@headrulefalse}
+
+\newif\if@footrule
+\newif\ifrun@footrule
+
+\def\firstpagefootrule{\@footruletrue}
+\def\nofirstpagefootrule{\@footrulefalse}
+
+\def\runningfootrule{\run@footruletrue}
+\def\norunningfootrule{\run@footrulefalse}
+
+\def\footrule{\@footruletrue\run@footruletrue}
+\def\nofootrule{\@footrulefalse\run@footrulefalse}
+
+% Initialize:
+
+\noheadrule
+\nofootrule
+
+% Cover page headrules and footrules:
+
+\newif\ifcov@headrule
+\newif\ifcovrun@headrule
+
+\def\coverfirstpageheadrule{\cov@headruletrue}
+\def\nocoverfirstpageheadrule{\cov@headrulefalse}
+
+\def\coverrunningheadrule{\covrun@headruletrue}
+\def\nocoverrunningheadrule{\covrun@headrulefalse}
+
+\def\coverheadrule{\cov@headruletrue\covrun@headruletrue}
+\def\nocoverheadrule{\cov@headrulefalse\covrun@headrulefalse}
+
+\newif\ifcov@footrule
+\newif\ifcovrun@footrule
+
+\def\coverfirstpagefootrule{\cov@footruletrue}
+\def\nocoverfirstpagefootrule{\cov@footrulefalse}
+
+\def\coverrunningfootrule{\covrun@footruletrue}
+\def\nocoverrunningfootrule{\covrun@footrulefalse}
+
+\def\coverfootrule{\cov@footruletrue\covrun@footruletrue}
+\def\nocoverfootrule{\cov@footrulefalse\covrun@footrulefalse}
+
+% Initialize:
+
+\nocoverheadrule
+\nocoverfootrule
+
+%--------------------------------------------------------------------
+%--------------------------------------------------------------------
+
+% \numpages, \iflastpage, and \oddeven
+% Also: \numpoints, \numquestions, \numparts, and \numsubparts
+% Also also: \pointsofquestion
+% Also: \numcoverpages and \totalnumpages
+
+% Make the number of pages available as the macro \numpages,
+% the number of points as \numpoints,
+% the number of questions as \numquestions,
+% the number of parts as \numparts, and
+% the number of subparts as \numsubparts
+
+% This was previously done with \pageref commands. When I stopped
+% using \pageref for this (in order to make this compatible with
+% hyperref.sty), this stuff was created:
+
+% \gdef commands for exam@lastpage, exam@numpoints,
+% exam@numbonuspoints, exam@numquestions, exam@numparts,
+% exam@numsubparts and exam@numsubsubparts are written to the .aux
+% file via \AtEndDocument.
+
+% \gdef commands for pointsofq@i, pointsofq@ii, etc. and
+% bonuspointsofq@i, bonuspointsofq@ii, etc. are written to the .aux
+% file as each question is completed (see the definition of the
+% questions environment).
+
+% \gdef commands for pointsonpage@i, pointsonpage@ii, etc. and
+% bonuspointsonpage@i, bonuspointsonpage@ii, etc. are written to the
+% .aux file as we encounter points defined for a later page, and for
+% the last such page with AtEndDocument.
+
+\def\numpages{\@ifundefined{exam@lastpage}%
+ {\mbox{\normalfont\bfseries ??}}%
+ \exam@lastpage
+}% numpages
+
+% Change 2011/04/01: We added a ``0'' in front of the
+% mbox when \exam@lastcoverpage isn't defined. This is
+% so that the construction \romannumeral\numcoverpages
+% won't generate an error on the first run of latex.
+\def\numcoverpages{\@ifundefined{exam@lastcoverpage}%
+ {0\mbox{\normalfont\bfseries ??}}%
+ \exam@lastcoverpage
+}% numpages
+
+\def\totalnumpages{\@ifundefined{exam@totalpages}%
+ {\mbox{\normalfont\bfseries ??}}%
+ \exam@totalpages
+}% numpages
+
+\def\numpoints{\@ifundefined{exam@numpoints}%
+ {\mbox{\normalfont\bfseries ??}}%
+ \exam@numpoints
+}% numpoints
+\def\numbonuspoints{\@ifundefined{exam@numbonuspoints}%
+ {\mbox{\normalfont\bfseries ??}}%
+ \exam@numbonuspoints
+}% numbonuspoints
+
+\def\numquestions{\@ifundefined{exam@numquestions}%
+ {\mbox{\normalfont\bfseries ??}}%
+ \exam@numquestions
+}% numquestions
+
+\def\numparts{\@ifundefined{exam@numparts}%
+ {\mbox{\normalfont\bfseries ??}}%
+ \exam@numparts
+}% numparts
+
+\def\numsubparts{\@ifundefined{exam@numsubparts}%
+ {\mbox{\normalfont\bfseries ??}}%
+ \exam@numsubparts
+}% numsubparts
+
+\def\numsubsubparts{\@ifundefined{exam@numsubsubparts}%
+ {\mbox{\normalfont\bfseries ??}}%
+ \exam@numsubsubparts
+}% numsubsubparts
+
+\def\pointsofquestion#1{\@ifundefined{pointsofq@\romannumeral #1}%
+ {\mbox{\normalfont\bfseries ??}}%
+ {\csname pointsofq@\romannumeral #1\endcsname}%
+}% pointsofquestion
+\def\bonuspointsofquestion#1{\@ifundefined{bonuspointsofq@\romannumeral #1}%
+ {\mbox{\normalfont\bfseries ??}}%
+ {\csname bonuspointsofq@\romannumeral #1\endcsname}%
+}% bonuspointsofquestion
+
+% For use in \combinedgradetable and \combinedpointtable, we're
+% changing the defintions of \pointsonpage and \bonuspointsonpage
+% so that when, e.g., pointsonpage@ii is undefined, we just produce
+% 0. This comes up because the final page of the exam may have either
+% points with no bonus point or bonus points with no points, in which
+% case the combined table will try to list both points and bonus points
+% for that last page, and one of those will be undefined.
+%
+% \def\pointsonpage#1{\@ifundefined{pointsonpage@\romannumeral #1}%
+% {\mbox{\normalfont\bfseries ??}}%
+% {\csname pointsonpage@\romannumeral #1\endcsname}%
+% }% pointsonpage
+% \def\bonuspointsonpage#1{\@ifundefined{bonuspointsonpage@\romannumeral #1}%
+% {\mbox{\normalfont\bfseries ??}}%
+% {\csname bonuspointsonpage@\romannumeral #1\endcsname}%
+% }% bonuspointsonpage
+%
+% spanish.ldf redefines \@roman, so we'll avoid using \roman:
+\def\pointsonpage#1{\@ifundefined{pointsonpage@\romannumeral #1}%
+ {0}%
+ {\csname pointsonpage@\romannumeral #1\endcsname}%
+}% pointsonpage
+\def\bonuspointsonpage#1{\@ifundefined{bonuspointsonpage@\romannumeral #1}%
+ {0}%
+ {\csname bonuspointsonpage@\romannumeral #1\endcsname}%
+}% bonuspointsonpage
+
+
+\newif\if@pointschanged
+\@pointschangedfalse
+
+\newcommand*{\CheckIfChanged@hlf}[2]{%
+ % The first argument is the name of a half counter.
+ % The second argument expands to the name (without the escape
+ % character, and not assumed to be defined) of the control sequence
+ % holding the previous value.
+ \@ifundefined{#2}%
+ {\global\@pointschangedtrue}%
+ {%
+ % OK; it's defined. See if it's changed:
+ \begingroup
+ \set@hlfcntr{tmp@hlfcntr}{\csname #2\endcsname}%
+ \edef\othpt@check{\prtaux@hlfcntr{tmp@hlfcntr}}%
+ \edef\pt@check{\prtaux@hlfcntr{#1}}%
+ \ifx \pt@check \othpt@check
+ % Do nothing
+ \else
+ \global\@pointschangedtrue
+ \fi
+ \endgroup
+ }%
+}% CheckIfChanged@hlf
+
+
+%%%\let\@realenddocument=\enddocument
+%%%\def\enddocument{\clearpage
+%%% \if@filesw
+%%% {\advance\c@page-1 \immediate\write\@mainaux
+%%% {\string\newlabel{@lastpage}{{}{\arabic{page}}}}%
+%%% }
+%%% \fi
+%%% \@realenddocument
+%%%}
+
+\AtEndDocument{%
+ \clearpage
+ \if@filesw
+ \advance\c@page-1
+ \immediate\write\@mainaux
+ {\string\gdef\string\exam@lastpage{\arabic{page}}}%
+ \immediate\write\@mainaux
+ {\string\gdef\string\exam@lastcoverpage{\arabic{num@coverpages}}}%
+ % We can now trash the value of num@coverpages:
+ \addtocounter{num@coverpages}{\value{page}}%
+ \immediate\write\@mainaux
+ {\string\gdef\string\exam@totalpages{\arabic{num@coverpages}}}%
+ \advance\c@page+1 % In case some other package looks at \c@page
+ %
+ \immediate\write\@mainaux
+ {\string\gdef\string\exam@numpoints{%
+ \prtaux@hlfcntr{numpoints}}}%
+ % See if this has changed from the last run of LaTeX:
+ \CheckIfChanged@hlf{numpoints}{exam@numpoints}%
+ \immediate\write\@mainaux
+ {\string\gdef\string\exam@numbonuspoints{%
+ \prtaux@hlfcntr{numbonuspoints}}}%
+ % See if this has changed from the last run of LaTeX:
+ \CheckIfChanged@hlf{numbonuspoints}{exam@numbonuspoints}%
+ \immediate\write\@mainaux
+ {\string\gdef\string\exam@numquestions{\thenumquestions}}%
+ \immediate\write\@mainaux
+ {\string\gdef\string\exam@numparts{\thenumparts}}%
+ \immediate\write\@mainaux
+ {\string\gdef\string\exam@numsubparts{\thenumsubparts}}%
+ \immediate\write\@mainaux
+ {\string\gdef\string\exam@numsubsubparts{\thenumsubsubparts}}%
+ \ifnum \thepageof@pagepoints > 0\relax
+ \immediate\write\@mainaux
+ {\string\gdef\string\pointsonpage@\romannumeral
+ \csname c@pageof@pagepoints\endcsname
+ {\prtaux@hlfcntr{@pagepoints}}}%
+ % See if this has changed from the last run of LaTeX:
+ \CheckIfChanged@hlf{@pagepoints}{pointsonpage@\romannumeral
+ \csname c@pageof@pagepoints\endcsname}%
+ \fi
+ \ifnum \thepageof@pagebonuspoints > 0\relax
+ \immediate\write\@mainaux
+ {\string\gdef\string\bonuspointsonpage@\romannumeral
+ \csname c@pageof@pagebonuspoints\endcsname
+ {\prtaux@hlfcntr{@pagebonuspoints}}}%
+ \CheckIfChanged@hlf{@pagebonuspoints}{bonuspointsonpage@\romannumeral
+ \csname c@pageof@pagebonuspoints\endcsname}%
+ \fi
+ \immediate\write\@mainaux
+ {\string\gdef\string\lastpage@withpoints{\page@withpoints}}%
+ % See if this has changed from the last run of LaTeX:
+ \@ifundefined{lastpage@withpoints}%
+ {\global\@pointschangedtrue}%
+ {%
+ % OK; it's defined. See if it's changed:
+ \begingroup
+ \edef\othpt@check{\page@withpoints}%
+ \edef\pt@check{\lastpage@withpoints}%
+ \ifx \pt@check \othpt@check
+ % Do nothing
+ \else
+ \global\@pointschangedtrue
+ \fi
+ \endgroup
+ }%
+ \immediate\write\@mainaux
+ {\string\gdef\string\lastpage@withbonuspoints{\page@withbonuspoints}}%
+ % See if this has changed from the last run of LaTeX:
+ \@ifundefined{lastpage@withbonuspoints}%
+ {\global\@pointschangedtrue}%
+ {%
+ % OK; it's defined. See if it's changed:
+ \begingroup
+ \edef\othpt@check{\page@withbonuspoints}%
+ \edef\pt@check{\lastpage@withbonuspoints}%
+ \ifx \pt@check \othpt@check
+ % Do nothing
+ \else
+ \global\@pointschangedtrue
+ \fi
+ \endgroup
+ }%
+ \fi
+ % Echo numbers of questions, parts, and subparts:
+ \typeout{This exam contains \thenumquestions\space questions
+ with \thenumparts\space parts, \thenumsubparts\space subparts,
+ and \thenumsubsubparts\space subsubparts.}
+ % If counting points, echo total points:
+ \if@printtotalpoints
+ \begingroup
+ \def\typ@expnd{%
+ \thenumpoints
+ \ifnumpoints@half
+ \space and a half%
+ \fi
+ }
+ \typeout{This exam has a total of \typ@expnd\space points.}
+ \def\typ@expnd{%
+ \thenumbonuspoints
+ \ifnumbonuspoints@half
+ \space and a half%
+ \fi
+ }
+ \typeout{This exam has a total of \typ@expnd\space bonus points.}
+ \endgroup
+ \fi
+ \if@pointschanged
+ \ClassWarningNoLine{exam}{Point totals have changed.
+ Rerun to get point totals right}%
+ \fi
+}% AtEndDocument
+
+
+% We define \iflastpage so that it can safely be used
+% in headers and footers:
+\def\iflastpage#1#2{%
+ \@ifundefined{exam@lastpage}{\def\@@lastpage{-1}}%
+ {\edef\@@lastpage{\exam@lastpage}}%
+ \ifnum\value{page}=\@@lastpage\relax
+ #1%
+ \else
+ #2%
+ \fi
+}% iflastpage
+
+
+% The macro \oddeven takes two arguments. If the page number is odd,
+% then you get the first argument; otherwise, you get the second
+% argument.
+\def\oddeven#1#2{%
+ \ifodd\value{page}%
+ #1%
+ \else
+ #2%
+ \fi
+}% oddeven
+
+
+
+%--------------------------------------------------------------------
+%--------------------------------------------------------------------
+% \ifcontinuation, \ContinuedQuestion,
+% \ifincomplete, and \IncompleteQuestion
+
+% The commands \ifcontinuation, \ContinuedQuestion, \ifincomplete, and
+% \IncompleteQuestion assume that there is only one questions
+% environment in the entire document. (Actually, \ContinuedQuestion
+% should work even if there are multiple questions environments, but
+% none of the other three will work in general.)
+
+
+
+% \PgInfo@write, \PgInfo and \PgInfo@get are our replacements
+% for \label, \newlabel, and \pageref. (We're avoiding using
+% \label, \newlabel, and \pageref so that we will be compatible
+% with hyperref.sty, which redefines those commands.)
+
+% We use \PgInfo, \PgInfo@write, and \PgInfo@get to know on which page
+% each question, part, subpart, subsubpart, and choice appears.
+
+% We use \PgInfo@write to write \PgInfo commands to the .aux file. The
+% \PgInfo command takes two arguments: A question (or part, or subpart,
+% or subsubpart) label, and the number of the page on which it appears.
+
+% The label for a question is of the form `question@2' (if it's question
+% 2).
+
+% The label for a part is of the form `part@2@1' (if it's part a of
+% question 2).
+
+% The label for a subpart is of the form `subpart@2@1@3' (if it's
+% subpart iii of part a of question 2).
+
+% The label for a subsubpart is of the form `subsubpart@2@1@3@4' (if
+% it's subsubpart $\delta$ of subpart iii of part a of question 2).
+
+% Each question, part, subpart, subsubpart, and choice also gets a
+% \PgInfo@write command using a label of the form question2@object3 (if
+% it's the third object of the second question).
+
+% Inside one of the solution environments (solution, solutionorbox,
+% etc.) each part, subpart, subsubpart, and choice get only the
+% \PgInfo@write command using a label of the form question2@object3
+% (if it's the third object of the second question).
+
+% When read in from the .aux file, the \PgInfo command defines a
+% control sequence of the form `Pg@label' that expands to the page
+% number for the corresponding question. For example,
+% \PgInfo{subsubpart@2@1@3@4}{7} defines \csname
+% Pg@subsubpart@2@1@3@4\endcsname to expand to 7.
+
+% The \PgInfo@get{label} command returns the value of the macro Pg@label,
+% but it *doesn't* check whether that macro is defined. Thus, it's
+% important to check that the macro Pg@label is defined before giving the
+% command \PgInfo@get{label}.
+
+
+
+% The token list to a \write command isn't expanded until
+% it's shipped out. Since the argument to PgInfo@write
+% generally contains macros, we want to expand those macros
+% now, rather than waiting until this is shipped out, at which
+% point the macros may have different values. Thus, we use
+% \edef to force expansion of the argument, and we put
+% a \noexpand in front of \thepage so that the \thepage
+% will not be expanded now. (This may not get shipped out
+% until a later page, and so we want the \thepage to be expanded
+% only when it's shipped out.)
+% We use the \begingroup \endgroup pair so that our use
+% of \reserved@a won't affect its use anywhere else.
+\def\PgInfo@write#1{%
+ \begingroup
+ \edef\reserved@a{\write\@mainaux
+ {\string\PgInfo{#1}{\noexpand\thepage}}}%
+ \reserved@a
+ \endgroup
+}
+
+%\PgInfo commands are written to the .aux file by the \PgInfo@write
+%command; that's the only place that \PgInfo commands appear.
+\def\PgInfo#1#2{\expandafter\gdef\csname Pg@#1\endcsname{#2}}
+
+% Note: PgInfo@get assumes that the control sequence being
+% constructed is already defined; you have to make sure of this
+% *before* calling \Pginfo@get
+\def\PgInfo@get#1{\csname Pg@#1\endcsname}
+
+% \set@counter@to@pageof takes two arguments: The first is the name of a
+% counter, and the second (expands to) the label of a question, part,
+% subpart, subsubpart, or choice. If that label exists, then we set the
+% counter equal to the page on which the question (or part, etc.)
+% appears. If that label doesn't exist, we set the counter equal to -1.
+% (No labels exist on the first run of LaTeX on the file, and if a new
+% question (or part, etc.) was created by editing the file, then the
+% label will not exist until the run of LaTeX after that.)
+\def\set@counter@to@pageof#1#2{%
+ \@ifundefined{Pg@#2}%
+ {\setcounter{#1}{-1}}%
+ {\setcounter{#1}{\csname Pg@#2\endcsname}}%
+}
+
+
+%--------------------------------------------------------------------
+
+% \ifcontinuation#1#2 expands to #2 if either:
+% (1) The command \noquestionsonthispage has been given on this page,
+% or
+% (2) The current page is before the page containing question number
+% 1, or
+% (3) A question begins on this page before any part, subpart,
+% subsubpart, or choice begins, or
+% (4) The current page is later than a page with the \nomorequestions
+% command.
+% Otherwise, it expands to #1.
+%
+% Thus, for example, if there are no questions, parts, subparts,
+% subsubparts, or choices on this page, and no \noquestionsonthispage
+% command, and we're not before question 1, and not after a
+% \nomorequestions command, then \ifcontinuation will expand to #1.
+
+
+\def\ifcontinuation#1#2{%
+ % If there's a \noquestionsonthispage command on this page, then
+ % we assume that we're not continuing anything:
+ \@ifundefined{No@Questions@Pg@\thepage}%
+ {\chk@contin{#1}{#2}}%
+ {#2}%
+}% \ifcontinuation
+
+\def\chk@contin#1#2{%
+ % We check whether we're on a page *before* the page on which the
+ % first question appears. If we don't yet know which page has
+ % question number 1, then we must be doing an early run of LaTeX,
+ % and we'll assume we're not a continuation.
+ \expandafter\ifx\csname Pg@question@1\endcsname\relax
+ % No page info yet; assume not a continuation
+ #2%
+ \else
+ % Note: The ``\relax'' at the end of the following \ifnum
+ % serves an entirely different purpose from the one at the
+ % end of the above \expandafter\ifx. That one (above)
+ % is one of the things being compared, whereas the
+ % one we're about to use is just to clearly mark the
+ % end of the second number being compared by the \ifnum
+ % (since it's conceivable that the ``#2'' would begin
+ % with a digit).
+ \ifnum \thepage < \csname Pg@question@1\endcsname\relax
+ % We're before the page with question 1:
+ #2%
+ \else
+ % The current page begins a new question if Contin@\thepage
+ % has been defined as a macro that expands to \relax (Note
+ % that this is different from if Contin@\thepage has never
+ % been defined at all, in which case it will be let equal to
+ % \relax (temporarily) by the \csname command.)
+ \expandafter\ifx\csname Contin@\thepage\endcsname\ref@relax
+ #2%
+ \else
+ % See if we're after a \nomorequestions command:
+ \@ifundefined{Pg@@endquestions}%
+ {#1}%
+ {\ifnum \thepage > \PgInfo@get{@endquestions}\relax
+ % We're after a \nomorequestions:
+ #2%
+ \else
+ % We actually are incomplete:
+ #1%
+ \fi
+ }%
+ \fi
+ \fi
+ \fi
+}% chk@contin
+
+\def\nomorequestions{%
+ \PgInfo@write{@endquestions}%
+}% nomorequestions
+
+\def\noquestionsonthispage{%
+ \write\@mainaux{\string\expandafter\string\gdef
+ \string\csname\space No@Questions@Pg@\thepage\string\endcsname
+ {No questions here}}%
+}% noquestionsonthispage
+
+
+%--------------------------------------------------------------------
+% \ContinuedQuestion is for use in headers and footers, where we can
+% assume that \thepage is the number of the page on which we'll
+% actually appear.
+
+% \ContinuedQuestion expands to the number of the question that
+% continues onto this page, or to -1 if this page begins with a new
+% question.
+
+% ACTUALLY: \ContinuedQuestion expands to a positive number if either
+% (1) this page doesn't contain the beginning of any question, part,
+% subpart, subsubpart, or choice, or (2) this page has a part, subpart,
+% subsubpart, or choice that appears before any question. That means
+% that if the current page actually begins with space for a continuation
+% of the previous question (but doesn't begin any part, subpart,
+% subsubpart, or choice of that question) and then has a question, then
+% we'll be asserting that this page begins with a new question, but the
+% actual top of the page will begin with some blank space that's
+% intended for the previous question.
+
+% \ContinuedQuestion works by examining the value of the macro
+% Contin@\thepage. If this page starts with a question (i.e., if no
+% question continues onto this page), then the macro Contin@\thepage
+% will be defined, and will expand to `\relax' (and so an \ifx between
+% \csname Contin@\thepage\endcsname and \ref@relax will be true).
+
+% If Contin@\thepage is undefined, then when it is used in an \ifx
+% command it will be temporarily set equal to \relax (which is
+% *different* from being a macro that expands to \relax); in this case,
+% there is no question, part, subpart, subsubpart, or choice that begins
+% on this page, and so \ContinuedQuestion will be set equal to the last
+% question that was begun on a page before this one.
+
+% The last possibility is that this page begins with either a part,
+% subpart, subsubpart, or choice. In this case, Contin@\thepage is
+% defined, and it expands to the number of the question that is
+% continued onto this page.
+
+
+\def\ref@relax{\relax}
+
+\def\ContinuedQuestion{%
+ \expandafter\ifx\csname Contin@\thepage\endcsname\relax
+ % We get here if there's no question, part, subpart,
+ % subsubpart, or choice on this page, and so Contin@\thepage has
+ % never been defined at all. In that case, this page
+ % continues whichever question was last begun on or
+ % before this page.
+ \find@latestques
+ \thelatest@ques
+ \else
+ \expandafter\ifx\csname Contin@\thepage\endcsname\ref@relax
+ % We get here if this page begins with a new question,
+ % which is why Contin@\thepage has been defined to be
+ % a macro that expands to \relax.
+ % ACTUALLY: We get here if this page has a question that
+ % appears before any part, subpart, subsubpart, or choice. That
+ % means that if the current page actually begins with space
+ % for a continuation of the previous question but doesn't begin
+ % any part, subpart, subsubpart, or choice of that question, then
+ % we'll be asserting that this page begins with a new question,
+ % but the actual top of the page will begin with some space
+ % that's intended for the previous question.
+ -1\relax
+ \else
+ % We get here if we didn't get anywhere above. This happens
+ % if Contin@\thepage has been defined to be a macro that expands
+ % to something other than \relax, in which case it has been
+ % defined to be a macro that expands to the number of the
+ % question that continues onto this page.
+ \csname Contin@\thepage\endcsname
+ \fi
+ \fi
+}
+
+%--------------------------------------------------------------------
+% \find@latestques is for use in headers and footers, where we can
+% assume that \thepage actually equals the page on which we'll appear.
+% We find the last question that was started on or before the current
+% page.
+
+\newcounter{latest@ques}
+
+\newcommand\find@latestques{%
+ % \find@latestques is for use in headers and footers.
+ % \find@latestques will set the counter latest@ques
+ % to the number of the last question
+ % that was begun on the exam from page 1 through the current
+ % page. This may well be the value of the question counter,
+ % but it may be less than that if the page following this one
+ % begins a new question and that question beginning was
+ % typeset before the present page was shipped out.
+ % Note: This macro is called both by \ContinuedQuestion and by
+ % \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 > \value{question}\relax
+ % Oops; probably because we're before the first question
+ % Just set latest@ques to -1:
+ \setcounter{latest@ques}{-1}%
+ \else
+ % If question latest@ques actually begins on this page (rather
+ % than on the next page, but early enough on the next page
+ % that the counter was advanced before we ran off into the
+ % output routine to output the page and set the header and
+ % footer), then that's the correct question number.
+ \expandafter\ifx\csname Pg@question@\arabic{question}\endcsname\relax
+ % We don't know what page that question is on;
+ % this must be an early run, before the aux file
+ % is helpful. Just set it equal to -1 and wait until the next
+ % run to get it right.
+ \setcounter{latest@ques}{-1}%
+ \else
+ % We now know that \PgInfo@get can tell us the page number
+ % of \arabic{question} and of all earlier questions.
+ % Set latest@ques equal to the current question number, and
+ % 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}{\value{question}}%
+ \decr@latest@ques
+ \fi
+ \fi
+}
+
+\def\decr@latest@ques{%
+ % If we get here, then we've already checked that the reference
+ % Pg@question@\thelatest@ques is defined at least for a value of
+ % \thelatest@ques greater than or equal to it's present value,
+ % so we assume it's defined for all lesser values as well:
+ \ifnum \thepage < \PgInfo@get{question@\thelatest@ques}\relax
+ % Nope; latest@ques starts on a later page
+ % Decrement latest@ques and see if that one's right:
+ \addtocounter{latest@ques}{-1}%
+ \ifnum \thelatest@ques < 1\relax
+ \setcounter{latest@ques}{-1}%
+ \let\next@dlq=\relax
+ \else
+ \let\next@dlq=\decr@latest@ques
+ \fi
+ \else
+ % latest@ques starts on this page or earlier, so
+ % that's the correct question number! Exit:
+ \let\next@dlq=\relax
+ \fi
+ \next@dlq
+}
+
+%--------------------------------------------------------------------
+%--------------------------------------------------------------------
+%--------------------------------------------------------------------
+\newcounter{ques@end}
+\newcounter{last@object}
+
+\def\find@quesend{%
+ % We find the last question started on or before the current page
+ % and then find the page containing the last part (or subpart, or
+ % subsubpart, or choice) of that question, and set the counter
+ % ques@end to that page number.
+ % Set latest@ques equal to the correct question number:
+ \find@latestques
+ \ifnum \value{latest@ques} < 0\relax
+ % This must be an early run of LaTeX, before we have
+ % \PgInfo commands in the .aux file:
+ \setcounter{ques@end}{-1}%
+ \else
+ % We now know that this question has at least one object (since
+ % we know that latest@ques isn't negative).
+ % We'll find its highest numbered object by setting last@object
+ % equal to 2 and then calling \find@lastobject to recursively
+ % test whether that object number exists and, if so, incrementing
+ % last@object to test for a higher numbered one:
+ \setcounter{last@object}{2}%
+ \find@lastobject
+ \setcounter{ques@end}{\PgInfo@get{question\thelatest@ques
+ @object\thelast@object}}%
+ \fi
+}% find@quesend
+
+\def\find@lastobject{%
+ % We check whether this question has an object numbered last@object
+ % and recursively increment last@object to find the highest
+ % numbered value for which the object exists:
+ \@ifundefined{Pg@question\thelatest@ques @object\thelast@object}%
+ {\addtocounter{last@object}{-1}%
+ \let\nextfind@lastobject=\relax
+ }%
+ {\addtocounter{last@object}{1}%
+ \let\nextfind@lastobject=\find@lastobject
+ }%
+ \nextfind@lastobject
+}% find@lastobject
+
+
+
+%--------------------------------------------------------------------
+%--------------------------------------------------------------------
+%--------------------------------------------------------------------
+
+\newcounter{incmp@ques}
+
+\def\IncompleteQuestion{%
+ \Find@Incmp@ques
+ % If there's no incomplete question, the counter incmp@ques will be
+ % set to -1:
+ \theincmp@ques
+}
+
+\def\Find@Incmp@ques{%
+ % If we're on the last page, then there's no incomplete question:
+ \iflastpage{\setcounter{incmp@ques}{-1}}{\chk@incomp}%
+}% Find@Incmp@ques
+\newcounter{next@ques}
+\newcounter{next@page}
+
+\def\chk@incomp{%
+ % If we get here, we're not on the last page.
+ % \find@quesend calls \find@latestques to set the counter
+ % latest@ques equal to the number of the last question begun on or
+ % before the current page, and then it sets the counter ques@end to
+ % the page containing the last ques@object of that question:
+ \find@quesend
+ \ifnum \theques@end > \thepage\relax
+ % This question has a part (or sub...) starting on a later page
+ \setcounter{incmp@ques}{\value{latest@ques}}%
+ \else
+ \chk@incompi
+ \fi
+}% chk@incomp
+
+\def\chk@incompi{%
+ % If there are any pages after the current one and before the next
+ % question (if there is a next question) that lack a
+ % \noquestionsonthispage and that aren't following the page of a
+ % \nomorequestions command, then question latest@ques is incomplete.
+ % Otherwise, there is no incomplete question:
+ \setcounter{next@ques}{\thelatest@ques}%
+ \addtocounter{next@ques}{1}%
+ % We use next@page as a scratch counter. We start by setting it
+ % to the last page we want to check for a \noquestionsonthispage
+ % command:
+ \expandafter\ifx\csname Pg@question@\thenext@ques \endcsname\relax
+ % This isn't the last page but there is no next question:
+%
+ \@ifundefined{exam@lastpage}%
+ {\setcounter{next@page}{-1}}%
+ {\setcounter{next@page}{\exam@lastpage}}%
+%
+% \setcounter{next@page}{\exam@lastpage}%
+ \else
+ \setcounter{next@page}{\PgInfo@get{question@\thenext@ques}}%
+ \addtocounter{next@page}{-1}%
+ \fi
+ % See if that's after a \nomorequestions command:
+ \@ifundefined{Pg@@endquestions}%
+ {}%
+ {\ifnum \PgInfo@get{@endquestions} < \value{next@page}\relax
+ \setcounter{next@page}{\PgInfo@get{@endquestions}}%
+ \fi
+ }%
+ % OK, the counter next@page now contains the last page to check.
+ \chk@incompii
+}% chk@incompi
+
+\def\chk@incompii{%
+ \ifnum \value{next@page} > \value{page}\relax
+ % We need to check the page next@page:
+ \@ifundefined{No@Questions@Pg@\arabic{next@page}}%
+ {\setcounter{incmp@ques}{\value{latest@ques}}%
+ \let\next@incompii=\relax
+ }%
+ {\addtocounter{next@page}{-1}%
+ \let\next@incompii = \chk@incompii
+ }%
+ \else
+ % There's no incomplete question:
+ \setcounter{incmp@ques}{-1}%
+ \let\next@incompii=\relax
+ \fi
+ \next@incompii
+}% chk@incompii
+
+\def\ifincomplete#1#2{%
+ % We need to pass the arguments to \chk@ifincomp; we save them in
+ % macros so they won't be messed up by the call to \Find@Incmp@ques:
+ \def\incomp@first{#1}%
+ \def\incomp@second{#2}%
+ % If there's a \noquestionsonthispage command on this page, then
+ % we assume nothing from this page is incomplete:
+ \@ifundefined{No@Questions@Pg@\thepage}%
+ {\chk@ifincomp}%
+ {\incomp@second}%
+}% ifincomplete
+
+\def\chk@ifincomp{%
+ \Find@Incmp@ques
+ % If there's no incomplete question, \Find@Incmp@ques sets the
+ % counter incmp@ques to -1:
+ \ifnum \theincmp@ques < 0\relax
+ \incomp@second
+ \else
+ % Are we after a page with \nomorequestions?
+ \@ifundefined{Pg@@endquestions}%
+ {\incomp@first}%
+ {\ifnum \thepage < \PgInfo@get{@endquestions}\relax
+ \incomp@first
+ \else
+ \incomp@second
+ \fi
+ }%
+ \fi
+}% chk@ifincomp
+
+%--------------------------------------------------------------------
+% These are the commands for dealing with hlfcntr's, i.e., the things
+% used to count points.
+%
+% A point value is a nonnegative integer with an optional half integer.
+% A hlfcntr consists of a regular counter together with an \if: If the
+% regular counter is called ``counter'', then the \if is called
+% ``\ifcounter@half''; it's set true by ``\counter@halftrue'' and set
+% false by ``\counter@halffalse''.
+%
+% The commands:
+%
+% \new@hlfcntr{countername}
+% \set@hlfcntr{countername}{value}
+% \copy@hlfcntr{tocounter}{fromcounter}
+% \addto@hlfcntr{countername}{value}
+% \add@hlfcntrtohlfcntr{getsaddedto}{whatsadded}
+% \ifhlfcntr@pos{countername}
+% \prtaux@hlfcntr{countername}
+% \prt@hlfcntr{countername}
+%
+% ``value'' can be either a (nonnegative) integer, an integer followed by
+% ``\half'', or just plain ``\half''. (Actually, ``value'' can be empty
+% (although the braces must be present), in which case it's interpreted
+% as ``0''.)
+%
+% Examples of valid values:
+% 0
+% 0\half
+% 1
+% 1\half
+% 2
+% 2\half
+% etc.
+
+% Note on using ``\global'': LaTeX's \setcounter and \addtocounter
+% commands are already \global (i.e., you don't have to say
+% ``\global''), but \somethingtrue and \somethingfalse (used to set
+% \ifsomething) aren't. Thus, we need to say ``\global'' when setting
+% these things.
+
+% To create a hlfcntr:
+\newcommand*\new@hlfcntr[1]{%
+ \newcounter{#1}%
+ \expandafter\newif\csname if#1@half\endcsname
+}% new@hlfcntr
+
+
+% A scratch hlfcntr:
+\new@hlfcntr{tmp@hlfcntr}
+
+\newcommand*\horiz@half{$\frac{1}{2}$}
+\newcommand*\slanted@half{%
+ $\raise0.6ex\hbox{$\scriptstyle 1$}\kern -.2em/\kern -.2em
+ \raise-0.5ex\hbox{$\scriptstyle 2$}$%
+}% slanted@half
+\newcommand*\useslantedhalf{\global\let\half\slanted@half}
+\newcommand*\usehorizontalhalf{\global\let\half\horiz@half}
+\newcommand*\half{\slanted@half}
+
+
+\newcommand*\set@hlfcntr[2]{%
+ \begingroup
+ \global\csname #1@halffalse\endcsname
+ % If there as a `\half' present, it will be executed
+ % right after the assignment of the digit part of #2
+ % to the counter #1.
+ \def\half{%
+ \global\csname #1@halftrue\endcsname
+ }%
+ % We insert a `0' in case there are no digits present:
+ % We avoid using \setcounter, because calc.sty redefines
+ % \setcounter in a way that conflicts with the \half trick
+ % we're using:
+ % \setcounter{#1}{0#2}\relax
+ \global\csname c@#1\endcsname 0#2\relax
+ \endgroup
+}% set@hlfcntr
+
+\newcommand*\copy@hlfcntr[2]{%
+ % We set #1 to the value of #2
+ \setcounter{#1}{\value{#2}}%
+ \csname if#2@half\endcsname
+ \global\csname #1@halftrue\endcsname
+ \else
+ \global\csname #1@halffalse\endcsname
+ \fi
+}% copy@hlfcntr
+
+\newcommand*\addto@hlfcntr[2]{%
+ % We add the valueandhalf #2 to hlfcntr #1
+ \begingroup
+ \def\half{\add@half{#1}}%
+ % We insert a `0' in case there are no digits present:
+ % We avoid using \addtocounter, because calc.sty redefines
+ % \addtocounter in a way that conflicts with the \half trick
+ % we're using:
+ % \addtocounter{#1}{0#2}\relax
+ \global\advance\csname c@#1\endcsname 0#2\relax
+ \endgroup
+}% addto@hlfcntr
+
+\newcommand*\add@hlfcntrtohlfcntr[2]{%
+ % We add the hlfcntr #2 to the hlfcntr #1
+ \addtocounter{#1}{\value{#2}}%
+ \csname if#2@half\endcsname
+ \add@half{#1}%
+ \fi
+}% add@hlfcntrtohlfcntr
+
+\newcommand*\add@half[1]{%
+ % We add one half to hlfcntr #1:
+ \csname if#1@half\endcsname
+ \addtocounter{#1}{1}%
+ \global\csname #1@halffalse\endcsname
+ \else
+ \global\csname #1@halftrue\endcsname
+ \fi
+}% add@half
+
+% Important reminder about \ifhlfcntr@pos: Do not use it inside
+% another conditional! The construction
+% \ifhlfcntr@pos{somecounter}
+% do some stuff...
+% \fi
+% is perfectly fine as long as it's expanded, but: If it's inside
+% another conditional, and the condition is not satisfied, then it's
+% read through without expansion. In that case, TeX sees the
+% \ifhlfcntr@pos but does *not* recognize it as being part of a
+% conditional, but when it sees the concluding \fi it does recognize
+% that, and so TeX completes the outer conditional at that \fi, which
+% causes an error.
+\newcounter{ifpos@cntr}
+\def\ifhlfcntr@pos#1{%
+ % The argument must be a hlfcntr (which, of course,
+ % can never be negative); we'll be true if and only if
+ % that halfcntr is positive:
+ \setcounter{ifpos@cntr}{\value{#1}}%
+ \csname if#1@half\endcsname
+ \addtocounter{ifpos@cntr}{1}%
+ \fi
+ \ifnum \value{ifpos@cntr} > 0\relax
+}% ifhlfnctr@pos
+
+% \prtaux@hlfcntr is used inside the argument of a \write command for
+% writing to the .aux file:
+\newcommand*\prtaux@hlfcntr[1]{%
+ % We don't want a \relax after the 0 in the following
+ % line, because it would sometimes appear in the aux file:
+ \ifnum \value{#1} = 0
+ % We have to make the following a macro, because if we
+ % don't do this part, the \fi will cause confusion, since
+ % there's no \if visible until the \csname is expanded:
+ \prtaux@halforzero{#1}%
+ \else
+ \arabic{#1}%
+ % We have to make the following a macro, because if we
+ % don't do this part, the \fi will cause confusion, since
+ % there's no \if visible until the \csname is expanded:
+ \prtaux@halforblank{#1}%
+ \fi
+}% prtaux@hlfcntr
+\newcommand*\prtaux@halforzero[1]{%
+ \csname if#1@half\endcsname
+ \string\half
+ \else
+ 0%
+ \fi
+}% prtaux@hlforzero
+\newcommand*\prtaux@halforblank[1]{%
+ \csname if#1@half\endcsname
+ \string\half
+ \fi
+}% prtaux@halforblank
+
+\newcommand*\prt@hlfcntr[1]{%
+ % We don't want a \relax after the 0 in the following
+ % line, because it would sometimes appear in the aux file:
+ \ifnum \value{#1} = 0
+ % We have to make the following a macro, because if we
+ % don't do this part, the \fi will cause confusion, since
+ % there's no \if visible until the \csname is expanded:
+ \prt@halforzero{#1}%
+ \else
+ \arabic{#1}%
+ % We have to make the following a macro, because if we
+ % don't do this part, the \fi will cause confusion, since
+ % there's no \if visible until the \csname is expanded:
+ \prt@halforblank{#1}%
+ \fi
+}% prt@hlfcntr
+\newcommand*\prt@halforzero[1]{%
+ \csname if#1@half\endcsname
+ \half
+ \else
+ 0%
+ \fi
+}% prt@hlforzero
+\newcommand*\prt@halforblank[1]{%
+ \csname if#1@half\endcsname
+ \half
+ \fi
+}% prt@halforblank
+
+% End of the commands for dealing with hlfcntr's
+
+%--------------------------------------------------------------------
+%---------------------------------------------------------------------
+%
+% ***************************
+% ** QUESTION ENVIRONMENTS **
+% ***************************
+%
+%
+%
+
+% We define the command \part only inside of a parts environment, so
+% that we don't interfere with the meaning of the standard article
+% documentclass command \part if that is used inside of a questions
+% environment. The commands \question, \subpart, and \subsubpart are
+% defined everywhere inside of a questions environment. If the user
+% accidentally gives a \subpart command outside of a subparts
+% environment, then an error will be created.
+
+
+
+
+% We use the counter name `partno' for the parts environment so that
+% we will not interfere with the counter `part' used by the article
+% document class.
+
+\newcounter{question}
+\newcounter{partno}
+\newcounter{subpart}
+\newcounter{subsubpart}
+\newcounter{choice}
+\new@hlfcntr{numpoints}
+\set@hlfcntr{numpoints}{0}
+\new@hlfcntr{numbonuspoints}
+\set@hlfcntr{numbonuspoints}{0}
+\new@hlfcntr{pointsof@thisquestion}
+\set@hlfcntr{pointsof@thisquestion}{0}
+\new@hlfcntr{bonuspointsof@thisquestion}
+\set@hlfcntr{bonuspointsof@thisquestion}{0}
+\newcounter{numquestions}
+\newcounter{numparts}
+\newcounter{numsubparts}
+\newcounter{numsubsubparts}
+\newcounter{Curr@Page}
+
+% @pagepoints accumulates the points on a single page:
+\new@hlfcntr{@pagepoints}
+\set@hlfcntr{@pagepoints}{0}
+\new@hlfcntr{@pagebonuspoints}
+\set@hlfcntr{@pagebonuspoints}{0}
+\newcounter{pageof@pagepoints}
+\setcounter{pageof@pagepoints}{0}
+\newcounter{pageof@pagebonuspoints}
+\setcounter{pageof@pagebonuspoints}{0}
+
+% latest@points is a holding area for points until we know
+% whether they'll land on the same page as the points
+% currently counted in @pagepoints:
+\new@hlfcntr{latest@points}
+\set@hlfcntr{latest@points}{0}
+\new@hlfcntr{latest@bonuspoints}
+\set@hlfcntr{latest@bonuspoints}{0}
+
+% Whenever we meet a new page on which points are defined, we'll
+% redefine \page@withpoints to expand to that page. At the end of the
+% document, it will hold the last page that has points, and we'll write
+% a \gdef\lastpage@withpoints command to the .aux file.
+% We initialize \page@withpoints here:
+\def\page@withpoints{0}%
+\def\page@withbonuspoints{0}%
+
+% \pageinfo@commands is used by each question, part, subpart, and
+% subsubpart to insert into everypar the \PgInfo@write command to put
+% its page number into the .aux file, the \PgInfo@get command to read
+% the page number into the counter Curr@Page, and to test and set
+% \Contin@\theCurr@Page. \temp@toks is used by part, subpart, and
+% subsubpart to append all that to \pageinfo@commands, rather than
+% deleting whatever may have been put into \pageinfo@commands by the
+% current question and/or part and/or subpart.
+\newtoks\pageinfo@commands
+\newtoks\temp@toks
+
+% \pagepoint@commands holds the commands to manage the counting of the
+% number of points defined on each page.
+\newtoks\pagepoint@commands
+
+% \point@toks holds the commands to print the points at the proper
+% location on the page (except that it's not used by the \qformat
+% option).
+\newtoks\point@toks
+
+% We'll use \greeknum to number subsubparts
+\def\greeknum#1{\expandafter\lc@greek\csname c@#1\endcsname}
+\def\lc@greek#1{%
+ \ifcase #1\or $\alpha$\or $\beta$\or $\gamma$\or $\delta$\or
+ $\epsilon$\or $\zeta$\or $\eta$\or $\theta$\or $\iota$\or
+ $\kappa$\or $\lambda$\or $\mu$\or $\nu$\or $\xi$\or o\or $\pi$\or
+ $\rho$\or $\sigma$\or $\tau$\or $\upsilon$\or $\phi$\or $\chi$\or
+ $\psi$\or $\omega$\else \@ctrerr
+ \fi
+}% lc@greek
+
+
+% The following macros are a variation on a trick from Victor
+% Eijkhout's ``TeX by Topic'', page 142:
+% Both \prepend@toklist and \append@toklist take two arguments,
+% both of which should be token lists.
+% \prepend@toklist prepends #2 to #1
+% \append@toklist appends #2 to #1
+
+\def\prepend@toklist#1#2{%
+ \edef\do@it{\noexpand#1={\the#2\the#1}}%
+ \do@it
+}% prepend@toklist
+
+\def\append@toklist#1#2{%
+ \edef\do@it{\noexpand#1={\the#1\the#2}}%
+ \do@it
+}% append@toklist
+
+
+% The command \qformat is provided for the user who wants to
+% design a nonstandard question line. If this command is used,
+% then the usual line containing the question number and the beginning
+% of the question will be replaced by the line specified by the
+% \qformat command, and the question will begin on the following
+% line.
+% Within the argument of the \qformat command:
+% \thequestion will be replaced by the question number, and
+% \thepoints will be replaced by ``\@points \@pointname'' if the
+% number of points has been specified for this question, and otherwise
+% it inserts nothing at all. (The conditional @placepoints is used to
+% determine if there were points specified for this question.)
+% The argument to the \qformat command *must* contain some
+% stretch, i.e., at least one \hfil or \dotfill or ...
+%
+% The command \noqformat cancels the effect of \qformat and returns us
+% to the default situation.
+%
+% The commands \bonusqformat and \nobonusformat are analogous.
+\newif\if@qformat
+\@qformatfalse
+\newif\if@bonusqformat
+\@bonusqformatfalse
+
+\def\qformat#1{%
+ \global\@qformattrue
+ \gdef\@questionformat{#1}%
+}% qformat
+\def\bonusqformat#1{%
+ \global\@bonusqformattrue
+ \gdef\@bonusquestionformat{#1}%
+}% bonusqformat
+
+\newcommand\noqformat{%
+ \global\@qformatfalse
+}% noqformat
+\newcommand\nobonusqformat{%
+ \global\@bonusqformatfalse
+}% nobonusqformat
+
+
+% \thepoints is for use in either a \qformat command
+% or a \pointformat command (or a \bonusqformat command).
+% It needs to have the
+% \if@placepoints so that if it's used in a \qformat command
+% it won't print anything if there are no points:
+\newcommand\thepoints{%
+ \if@placepoints
+ \if@bonus
+ \@points \@bonuspointname
+ \else
+ \@points \@pointname
+ \fi
+ \fi
+}% thepoints
+
+% \themarginpoints is for use only in a \pointformat command,
+% and so it doesn't need the \if@placepoints bit in \thepoints:
+\newcommand\themarginpoints{%
+ \if@bonus
+ \@points \@marginbonuspointname
+ \else
+ \@points \@marginpointname
+ \fi
+}% themarginpoints
+
+% We define the \subpart and \subsubpart commands when we enter a
+% questions environment (rather than waiting until we enter a subparts
+% of subsubparts environment) so that we can signal an error if a
+% \subpart or \subsubpart command appears outside of the corresponding
+% environment. (We don't do this for the \part command so that the user
+% can use the standard sectioning \part command outside of a parts
+% environment.)
+
+% The counter ques@object will count the items in each question, where
+% an item is defined as either the question itself, or a part, or a
+% subpart, or a subsubpart, or a choice. This will be used by
+% \find@quesend to find the last page occupied by the last question
+% begun on or before the current page:
+\newcounter{ques@object}
+
+% \first@questionobject will be used by the \question command.
+% That is, it will be used only once, but we want to keep its
+% definition here, near the definitions of \addquestionobject and
+% \questionobject@pluspagecheck.
+\newcommand{\first@questionobject}{%
+ \setcounter{ques@object}{1}%
+ % \PgInfo@write expands it's argument, so we don't need edef:
+% \edef\q@object@label{%
+% question\arabic{question}@object\arabic{ques@object}}%
+% \PgInfo@write{\q@object@label}%
+ \PgInfo@write{question\arabic{question}@object\arabic{ques@object}}%
+}% first@questionobject
+
+% \addquestionobject will be used by each part, subpart, and
+% subsubpart, and can also be used by the user to mark the end of a
+% question that spills over onto the next page without any part,
+% subpart, etc. starting on that page:
+\newcommand{\addquestionobject}{%
+ \addtocounter{ques@object}{1}%
+ % \PgInfo@write expands it's argument, so we don't need edef:
+% \edef\q@object@label{%
+% question\arabic{question}@object\arabic{ques@object}}%
+% \PgInfo@write{\q@object@label}%
+ \PgInfo@write{question\arabic{question}@object\arabic{ques@object}}%
+}% addquestionobject
+
+% \questionobject@pluspagecheck will be used by each choice, as well
+% as by any \part, \subpart, or \subsubpart that's inside of a
+% solution. It uses the questionobject to check if we're the first
+% one on the current page, since choices (and questions etc. inside of
+% solution environments) don't have labels the way that questions,
+% parts, subparts, and subsubparts do (those things use the label to
+% check if they're the first thing on the page).
+\newcommand{\questionobject@pluspagecheck}{%
+ % We don't want to do any of this if we're both inside a solution
+ % environment and not printing answers (because we want to avoid
+ % incrementing ques@object):
+ \if@insolution
+ \ifprintanswers
+ \doqobj@ppchk
+ \fi
+ \else
+ \doqobj@ppchk
+ \fi
+}% questionobject@pluspagecheck
+\newcommand{\doqobj@ppchk}{%
+ \addtocounter{ques@object}{1}%
+ % We need the edef because we check the page of \q@object@label:
+ \edef\q@object@label{%
+ question\arabic{question}@object\arabic{ques@object}}%
+ \PgInfo@write{\q@object@label}%
+ \set@counter@to@pageof{Curr@Page}{\q@object@label}%
+ \expandafter\ifx\csname Contin@\theCurr@Page\endcsname\relax
+ % We're the first \question, \part, \subpart, \subsubpart,
+ % or choice on this page:
+ \global\expandafter\edef\csname
+ Contin@\theCurr@Page\endcsname{\arabic{question}}%
+ \fi
+}% doqobj@ppchk
+
+
+
+% if@bonus will be true when we're doing a bonusquestion or bonuspart
+% or etc., and it will also be used also to distinguish between
+% \gradetable and \bonusgradetable (and between \pointtable and
+% \bonuspointtable, etc.), and also to distinguish between
+% \pointsinrange and \bonuspointsinrange:
+\newif\if@bonus
+\@bonusfalse
+
+% The following are for advanced users who want to customize the list
+% parameters (\topsep, \partopsep, \itemsep, \parsep, etc.) for the
+% lists that these environments create. They are all defined to be
+% empty, but the user can change them using \renewcommand.
+\newcommand\questionshook{}
+\newcommand\partshook{}
+\newcommand\subpartshook{}
+\newcommand\subsubpartshook{}
+\newcommand\choiceshook{}
+\newcommand\checkboxeshook{}
+
+
+\newenvironment{questions}{%
+ % \@queslevel is used for two purposes:
+ % (1) We check that every \question, \part, \subpart, and
+ % \subsubpart command appears inside the appropriate environment,
+ % and generate an error if one appears in the wrong place.
+ % (2) If a \qformat is being used and if \@queslevel tells us
+ % that we're currently processing a question, then we set
+ % \global \point@toks={} to avoid setting the points for a
+ % question other than via the qformat command.
+ \def\@queslevel{question}%
+ \def\titledquestion##1{%
+ \@bonusfalse
+ \def\thequestiontitle{##1}%
+ \process@question
+ }%
+ \def\bonustitledquestion##1{%
+ \@bonustrue
+ \def\thequestiontitle{##1}%
+ \process@question
+ }%
+ \def\question{%
+ \@bonusfalse
+ \def\thequestiontitle{\csname p@question\endcsname
+ \csname thequestion\endcsname}%
+ \process@question
+ }%
+ \def\bonusquestion{%
+ \@bonustrue
+ \def\thequestiontitle{\csname p@question\endcsname
+ \csname thequestion\endcsname}%
+ \process@question
+ }%
+ \def\process@question{%
+ \if@coverpages
+ \cover@question@error
+ \fi
+ \@checkqueslevel{question}%
+ \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 \value{question} is the
+ % number of the question that was just completed.)
+ \if@filesw
+ \ifnum \value{question} > 0\relax
+ % First do regular points:
+ \immediate\write\@mainaux
+ {\string\gdef\string\pointsofq@
+ \romannumeral \csname c@question\endcsname
+ {\prtaux@hlfcntr{pointsof@thisquestion}}}%
+ % See if this has changed from the last run of LaTeX:
+ \CheckIfChanged@hlf{pointsof@thisquestion}{pointsofq@\romannumeral
+ \csname c@question\endcsname}%
+ % Now do bonus points:
+ \immediate\write\@mainaux
+ {\string\gdef\string\bonuspointsofq@
+ \romannumeral \csname c@question\endcsname
+ {\prtaux@hlfcntr{bonuspointsof@thisquestion}}}%
+ % See if this has changed from the last run of LaTeX:
+ \CheckIfChanged@hlf{bonuspointsof@thisquestion}%
+ {bonuspointsofq@\romannumeral
+ \csname c@question\endcsname}%
+ \fi
+ \fi
+ \set@hlfcntr{pointsof@thisquestion}{0}%
+ \set@hlfcntr{bonuspointsof@thisquestion}{0}%
+ % If there was a question with points immediately preceding
+ % this question (i.e., there were no parts in the previous
+ % question), then @placepoints will still be true, and we need to
+ % cancel it. (We used to do this inside of the \thepoints macro,
+ % but that allowed for an error if the user specified points for a
+ % question but had a \qformat that didn't mention \thepoints.) We
+ % also set @placepoints to be false when entering a parts
+ % environment.
+ \global \@placepointsfalse
+ % point@toks will normally be empty at this point, but it might be
+ % nonempty if there were points somewhere in the previous question
+ % that never made it onto the page because we never entered
+ % horizontal mode (perhaps because the user was weird and let the
+ % text of a question (or part, etc.) consist entirely of an
+ % enumerate environment, or description environment, or etc.).
+ \global \point@toks={}%
+ % Important: Don't leave any blank lines inside of
+ % \pageinfo@commands!! This token list will be dumped into
+ % horizontal mode by \everypar, and so any blank lines will
+ % cause paragraph breaks.
+ \pageinfo@commands={%
+ \edef\@queslabel{question@\arabic{question}}%
+ \PgInfo@write{\@queslabel}%
+ \first@questionobject
+ % In addition to the \PgInfo@write we use an actual \label
+ % command. We do this in order to make the question numbers in
+ % the grade tables into \ref's, so that if the user says
+ % \usepackage{hyperref}, those question numbers will be clickable.
+ %
+ % A further purpose of these labels (which we actually do for all
+ % questions, parts, subparts, and subsubparts) is that if a
+ % question (or part, etc.) is, e.g., moved from one page to
+ % another, LaTeX will notice this and warn the user that LaTeX
+ % must be run one more time to be sure everything is correct.
+ %
+ % We need to do this even though we've already included code to
+ % check when point totals change because questions (and parts,
+ % etc.) know what page they're on from reading the info written to
+ % the .aux file on the previous run. Thus, if a question (or
+ % part, etc.) is moved to a different page, then the pointsonpage
+ % totals won't notice until the *second* subsequent run of LaTeX,
+ % and so there'll be no warning to the user on the *first* run.
+ % Including these labels gives the user a warning on that first
+ % run.
+ %
+ % Further futzing required: Since this is being put into the
+ % token list \pageinfo@commands, the contents of which won't
+ % actually be executed until we enter horizontal mode (which
+ % may well be in the first part of a parts environment), we need
+ % to make sure that \@currentlabel is the number of the
+ % question:
+ \begingroup % to confine the change to \@currentlabel
+% \def\@currentlabel{\csname p@question\endcsname
+% \csname thequestion\endcsname}%
+ \def\@currentlabel{\thequestiontitle}%
+ \label{\@queslabel}%
+ \endgroup
+ \set@counter@to@pageof{Curr@Page}{\@queslabel}%
+ \expandafter\ifx\csname Contin@\theCurr@Page\endcsname\relax
+ % We're the first \question, \part, \subpart, \subsubpart,
+ % or choice on this page:
+ \global\expandafter\edef
+ \csname Contin@\theCurr@Page\endcsname{\relax}%
+ \fi
+ \the\pagepoint@commands
+ \global \pageinfo@commands={}%
+ }% pageinfo@commands
+ \ifhmode
+ % Remove any skips at the end of the previous paragraph
+ % that might cause a blank line, and then end that paragraph:
+ \unskip\unskip \par
+ \fi
+ \@doitem
+ }% process@question
+ \def\subpart{%
+ \@bonusfalse
+ \process@subpart
+ }%
+ \def\bonussubpart{%
+ \@bonustrue
+ \process@subpart
+ }%
+ \def\process@subpart{%
+ \if@coverpages
+ \cover@question@error
+ \fi
+ \@checkqueslevel{subpart}%
+ \if@insolution
+ % We don't count this subpart, so no addtocounter{numsubparts}.
+ \temp@toks={%
+ \questionobject@pluspagecheck
+ \global \pageinfo@commands={}%
+ % We omit the pagepoint@commands
+ }% temp@toks
+ \else
+ \addtocounter{numsubparts}{1}%
+ % Important: Don't leave any blank lines inside of
+ % \pageinfo@commands!! This token list will be dumped into
+ % horizontal mode by \everypar, and so any blank lines will
+ % cause paragraph breaks.
+ \temp@toks={%
+ \edef\@subpartlabel{subpart@\arabic{question}%
+ @\arabic{partno}@\arabic{subpart}}%
+ \PgInfo@write{\@subpartlabel}%
+ \addquestionobject
+ % In addition to the \PgInfo@write we use an actual \label
+ % command. We do this in order to make the question numbers in
+ % the grade tables into \ref's, so that if the user says
+ % \usepackage{hyperref}, those question numbers will be clickable.
+ %
+ % A further purpose of these labels (which we actually do for all
+ % questions, parts, subparts, and subsubparts) is that if a
+ % question (or part, etc.) is, e.g., moved from one page to
+ % another, LaTeX will notice this and warn the user that LaTeX
+ % must be run one more time to be sure everything is correct.
+ %
+ % We need to do this even though we've already included code to
+ % check when point totals change because questions (and parts,
+ % etc.) know what page they're on from reading the info written to
+ % the .aux file on the previous run. Thus, if a question (or
+ % part, etc.) is moved to a different page, then the pointsonpage
+ % totals won't notice until the *second* subsequent run of LaTeX,
+ % and so there'll be no warning to the user on the *first* run.
+ % Including these labels gives the user a warning on that first
+ % run.
+ \label{\@subpartlabel}%
+ \set@counter@to@pageof{Curr@Page}{\@subpartlabel}%
+ \expandafter\ifx\csname Contin@\theCurr@Page\endcsname\relax
+ % We're the first \question, \part, \subpart, \subsubpart,
+ % or choice on this page:
+ \global\expandafter\edef\csname
+ Contin@\theCurr@Page\endcsname{\arabic{question}}%
+ \fi
+ \the\pagepoint@commands
+ \global \pageinfo@commands={}%
+ }% temp@toks
+ \fi
+ \append@toklist \pageinfo@commands \temp@toks
+ \ifhmode
+ % Remove any skips at the end of the previous paragraph
+ % that might cause a blank line, and then end that paragraph:
+ \unskip\unskip \par
+ \fi
+ \@doitem
+ }% process@subpart
+ \def\subsubpart{%
+ \@bonusfalse
+ \process@subsubpart
+ }%
+ \def\bonussubsubpart{%
+ \@bonustrue
+ \process@subsubpart
+ }%
+ \def\process@subsubpart{%
+ \if@coverpages
+ \cover@question@error
+ \fi
+ \@checkqueslevel{subsubpart}%
+ \if@insolution
+ % We don't count this subsubpart, so no addtocounter{numsubsubparts}.
+ \temp@toks={%
+ \questionobject@pluspagecheck
+ \global \pageinfo@commands={}%
+ % We omit the pagepoint@commands
+ }% temp@toks
+ \else
+ \addtocounter{numsubsubparts}{1}%
+ % Important: Don't leave any blank lines inside of
+ % \pageinfo@commands!! This token list will be dumped into
+ % horizontal mode by \everypar, and so any blank lines will
+ % cause paragraph breaks.
+ \temp@toks={%
+ \edef\@subsubpartlabel{subsubpart@\arabic{question}%
+ @\arabic{partno}@\arabic{subpart}@\arabic{subsubpart}}%
+ \PgInfo@write{\@subsubpartlabel}%
+ \addquestionobject
+ % In addition to the \PgInfo@write we use an actual \label
+ % command. We do this in order to make the question numbers in
+ % the grade tables into \ref's, so that if the user says
+ % \usepackage{hyperref}, those question numbers will be clickable.
+ %
+ % A further purpose of these labels (which we actually do for all
+ % questions, parts, subparts, and subsubparts) is that if a
+ % question (or part, etc.) is, e.g., moved from one page to
+ % another, LaTeX will notice this and warn the user that LaTeX
+ % must be run one more time to be sure everything is correct.
+ %
+ % We need to do this even though we've already included code to
+ % check when point totals change because questions (and parts,
+ % etc.) know what page they're on from reading the info written to
+ % the .aux file on the previous run. Thus, if a question (or
+ % part, etc.) is moved to a different page, then the pointsonpage
+ % totals won't notice until the *second* subsequent run of LaTeX,
+ % and so there'll be no warning to the user on the *first* run.
+ % Including these labels gives the user a warning on that first
+ % run.
+ \label{\@subsubpartlabel}%
+ \set@counter@to@pageof{Curr@Page}{\@subsubpartlabel}%
+ \expandafter\ifx\csname Contin@\theCurr@Page\endcsname\relax
+ % We're the first \question, \part, \subpart, \subsubpart,
+ % or choice on this page:
+ \global\expandafter\edef\csname
+ Contin@\theCurr@Page\endcsname{\arabic{question}}%
+ \fi
+ \the\pagepoint@commands
+ \global \pageinfo@commands={}%
+ }% temp@toks
+ \fi
+ \append@toklist \pageinfo@commands \temp@toks
+ \ifhmode
+ % Remove any skips at the end of the previous paragraph
+ % that might cause a blank line, and then end that paragraph:
+ \unskip\unskip \par
+ \fi
+ \@doitem
+ }% process@subsubpart
+ \list{\question@number}%
+ {\usecounter{question}%
+ % We use the default definition of \makelabel
+ % so as not to interfere with \qformat commands.
+ % \def\makelabel##1{\hss\llap{##1}}%
+ \settowidth{\leftmargin}{10.\hskip\labelsep}%
+ \labelwidth\leftmargin\advance\labelwidth-\labelsep
+ \partopsep=0pt
+ \questionshook
+ }%
+ }% End of the first argument of \newenvironment{questions}
+ {%
+ \endlist
+ % Write the number of points of the final question
+ % to the .aux file:
+ \if@filesw
+ \ifnum \value{question} > 0\relax
+ % First do the regular points:
+ \immediate\write\@mainaux
+ {\string\gdef\string\pointsofq@\romannumeral
+ \csname c@question\endcsname
+ {\prtaux@hlfcntr{pointsof@thisquestion}}}%
+ % See if this has changed from the last run of LaTeX:
+ \CheckIfChanged@hlf{pointsof@thisquestion}%
+ {pointsofq@\romannumeral
+ \csname c@question\endcsname}%
+ % Now do the bonus points:
+ \immediate\write\@mainaux
+ {\string\gdef\string\bonuspointsofq@\romannumeral
+ \csname c@question\endcsname
+ {\prtaux@hlfcntr{bonuspointsof@thisquestion}}}%
+ % See if this has changed from the last run of LaTeX:
+ \CheckIfChanged@hlf{bonuspointsof@thisquestion}%
+ {bonuspointsofq@\romannumeral
+ \csname c@question\endcsname}%
+ \fi
+ \fi
+ }% End of the second argument of \newenvironment{questions}
+
+% \question@number is used as the label in the question list (instead
+% of \questionlabel) so that if the user uses a \qformat command,
+% we'll use the \@questionformat specified by the \qformat command:
+
+\def\question@number{%
+ \if@bonus
+ \if@bonusqformat
+ \makebox[\hsize][s]{\@bonusquestionformat}\hskip-\labelsep
+ \else
+ \questionlabel
+ \fi
+ \else
+ \if@qformat
+ \makebox[\hsize][s]{\@questionformat}\hskip-\labelsep
+ \else
+ \questionlabel
+ \fi
+ \fi
+}
+\newcommand\questionlabel{\thequestion.}
+
+% We want the \part command to be defined *only* inside of a parts
+% environment, so that the user can use the standard sectioning \part
+% command inside of a questions environment (as long as it's outside of
+% a parts environment).
+
+\newenvironment{parts}{%
+ \def\@queslevel{part}%
+ % If the question numbers are being inserted via a \qformat,
+ % and if a question is beginning with a parts environment, then
+ % we need to enter horizonal mode to get the qformat printed
+ % on the page, rather than saving up the question label (and
+ % possible points) to be combined with the label of the first
+ % part. (\if@inlabel tells us if we are still waiting to enter
+ % horizontal mode after seeing a \question command.)
+ \if@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
+ \fi
+ \def\part{%
+ \@bonusfalse
+ \process@part
+ }%
+ \def\bonuspart{%
+ \@bonustrue
+ \process@part
+ }%
+ \def\process@part{%
+ \if@coverpages
+ \cover@question@error
+ \fi
+ \@checkqueslevel{part}%
+ \if@insolution
+ % We don't count this part, so no addtocounter{numparts}.
+ \temp@toks={%
+ \questionobject@pluspagecheck
+ \global \pageinfo@commands={}%
+ % We omit the pagepoint@commands
+ }% temp@toks
+ \else
+ \addtocounter{numparts}{1}%
+ % Important: Don't leave any blank lines inside of
+ % \pageinfo@commands!! This token list will be dumped into
+ % horizontal mode by \everypar, and so any blank lines will
+ % cause paragraph breaks.
+ \temp@toks={%
+ \edef\@partlabel{part@\arabic{question}@\arabic{partno}}%
+ \PgInfo@write{\@partlabel}%
+ \addquestionobject
+ % In addition to the \PgInfo@write we use an actual \label
+ % command. We do this in order to make the question numbers in
+ % the grade tables into \ref's, so that if the user says
+ % \usepackage{hyperref}, those question numbers will be clickable.
+ %
+ % A further purpose of these labels (which we actually do for all
+ % questions, parts, subparts, and subsubparts) is that if a
+ % question (or part, etc.) is, e.g., moved from one page to
+ % another, LaTeX will notice this and warn the user that LaTeX
+ % must be run one more time to be sure everything is correct.
+ %
+ % We need to do this even though we've already included code to
+ % check when point totals change because questions (and parts,
+ % etc.) know what page they're on from reading the info written to
+ % the .aux file on the previous run. Thus, if a question (or
+ % part, etc.) is moved to a different page, then the pointsonpage
+ % totals won't notice until the *second* subsequent run of LaTeX,
+ % and so there'll be no warning to the user on the *first* run.
+ % Including these labels gives the user a warning on that first
+ % run.
+ \label{\@partlabel}%
+ \set@counter@to@pageof{Curr@Page}{\@partlabel}%
+ \expandafter\ifx\csname Contin@\theCurr@Page\endcsname\relax
+ \global\expandafter\edef\csname
+ Contin@\theCurr@Page\endcsname{\arabic{question}}%
+ \fi
+ \the\pagepoint@commands
+ \global \pageinfo@commands={}%
+ }% temp@toks
+ \fi
+ \append@toklist \pageinfo@commands \temp@toks
+ \ifhmode
+ % Remove any skips at the end of the previous paragraph
+ % that might cause a blank line, and then end that paragraph:
+ \unskip\unskip \par
+ \fi
+ \@doitem
+ }% process@part
+ \list{\partlabel}%
+ {%
+ \usecounter{partno}\def\makelabel##1{\hss\llap{##1}}%
+ \settowidth{\leftmargin}{(m)\hskip\labelsep}%
+ \labelwidth\leftmargin\advance\labelwidth-\labelsep
+ \topsep=0pt
+ \partopsep=0pt
+ \partshook
+ }%
+ }% newenvironment{parts}
+ {\endlist}
+\newcommand\partlabel{(\thepartno)}
+\def\thepartno{\alph{partno}}
+
+\newenvironment{subparts}{%
+ \def\@queslevel{subpart}%
+ \list{\subpartlabel}%
+ {%
+ \usecounter{subpart}\def\makelabel##1{\hss\llap{##1}}%
+ \settowidth{\leftmargin}{vii.\hskip\labelsep}%
+ \labelwidth\leftmargin\advance\labelwidth-\labelsep
+ \topsep=0pt
+ \partopsep=0pt
+ \subpartshook
+ }%
+ }%
+ {\endlist}
+\newcommand\subpartlabel{\thesubpart.}
+\def\thesubpart{\roman{subpart}}
+
+\newenvironment{subsubparts}{%
+ \def\@queslevel{subsubpart}%
+ \list{\subsubpartlabel}%
+ {%
+ \usecounter{subsubpart}\def\makelabel##1{\hss\llap{##1}}%
+ \settowidth{\leftmargin}{($\psi$)\hskip\labelsep}%
+ \labelwidth\leftmargin\advance\labelwidth-\labelsep
+ \topsep=0pt
+ \partopsep=0pt
+ \subsubpartshook
+ }%
+ }%
+ {\endlist}
+\newcommand\subsubpartlabel{\thesubsubpart)}
+\def\thesubsubpart{\greeknum{subsubpart}}
+
+
+
+
+\pagepoint@commands={%
+ \ifhlfcntr@pos{latest@points}%
+ % We're putting a question (or part, etc.)
+ % with points onto this page:
+ \ifnum \theCurr@Page > \thepageof@pagepoints\relax
+ % These points go on a later page than
+ % the points currently counted in @pagepoints:
+ \ifnum \thepageof@pagepoints = 0\relax
+ % Do nothing...
+ \else
+ \immediate\write\@mainaux
+ {\string\gdef\string\pointsonpage@
+ \romannumeral \csname c@pageof@pagepoints\endcsname
+ {\prtaux@hlfcntr{@pagepoints}}}%
+ % See if this has changed from the last run of LaTeX:
+ \CheckIfChanged@hlf{@pagepoints}{pointsonpage@\romannumeral
+ \csname c@pageof@pagepoints\endcsname}%
+ \fi
+ % The following is a macro because \theCurr@Page and
+ % \thepageof@pagepoints might differ by more than 1:
+ \increment@pageof@pagepoints
+ % The following label is so that we can make the page
+ % numbers in a grade table indexed by page into \pageref's
+ % so that \usepackage{hyperref} and pdflatex will
+ % make them clickable:
+ \label{firstpoints@onpage@\arabic{Curr@Page}}%
+ \else
+ % These points go on the same page as the points
+ % currently counted in @pagepoints:
+ \add@hlfcntrtohlfcntr{@pagepoints}{latest@points}%
+ \set@hlfcntr{latest@points}{0}%
+ \fi
+ \fi
+ \ifhlfcntr@pos{latest@bonuspoints}%
+ % We're putting a question (or part, etc.)
+ % with bonus points onto this page:
+ \ifnum \theCurr@Page > \thepageof@pagebonuspoints\relax
+ % These bonus points go on a later page than
+ % the points currently counted in @pagebonuspoints:
+ \ifnum \thepageof@pagebonuspoints = 0\relax
+ % Do nothing...
+ \else
+ \immediate\write\@mainaux
+ {\string\gdef\string\bonuspointsonpage@
+ \romannumeral \csname c@pageof@pagebonuspoints\endcsname
+ {\prtaux@hlfcntr{@pagebonuspoints}}}%
+ % See if this has changed from the last run of LaTeX:
+ \CheckIfChanged@hlf{@pagebonuspoints}%
+ {bonuspointsonpage@\romannumeral
+ \csname c@pageof@pagebonuspoints\endcsname}%
+ \fi
+ % The following is a macro because \theCurr@Page and
+ % \thepageof@pagebonuspoints might differ by more than 1:
+ \increment@pageof@pagebonuspoints
+ % The following label is so that we can make the page
+ % numbers in a bonus grade table indexed by page into \pageref's
+ % so that \usepackage{hyperref} and pdflatex will
+ % make them clickable:
+ \label{firstbonuspoints@onpage@\arabic{Curr@Page}}%
+ \else
+ % These points go on the same page as the points
+ % currently counted in @pagebonuspoints:
+ \add@hlfcntrtohlfcntr{@pagebonuspoints}{latest@bonuspoints}%
+ \set@hlfcntr{latest@bonuspoints}{0}%
+ \fi
+ \fi
+}% pagepoint@commands
+\def\increment@pageof@pagepoints{%
+ \addtocounter{pageof@pagepoints}{1}%
+ \ifnum \theCurr@Page > \thepageof@pagepoints\relax
+ \immediate\write\@mainaux
+ {\string\gdef\string\pointsonpage@
+ \romannumeral \csname c@pageof@pagepoints\endcsname{0}}%
+ % See if this has changed from the last run of LaTeX:
+ \@ifundefined{pointsonpage@\romannumeral
+ \csname c@pageof@pagepoints\endcsname}
+ {\global\@pointschangedtrue}%
+ {%
+ % OK; it's defined. See if it's changed:
+ \begingroup
+ \set@hlfcntr{tmp@hlfcntr}{\csname pointsonpage@\romannumeral
+ \csname c@pageof@pagepoints\endcsname\endcsname}%
+ \edef\othpt@check{\prtaux@hlfcntr{tmp@hlfcntr}}%
+ \def\pt@check{0}%
+ \ifx \pt@check \othpt@check
+ % Do nothing
+ \else
+ \global\@pointschangedtrue
+ \fi
+ \endgroup
+ }%
+ \let\next@incr@pageof = \increment@pageof@pagepoints
+ \else
+ \copy@hlfcntr{@pagepoints}{latest@points}%
+ \set@hlfcntr{latest@points}{0}%
+ % \page@withpoints will be used to find the last
+ % page that has points, which will be written to
+ % the .aux file via \AtEndDocument:
+ \global\edef\page@withpoints{\thepageof@pagepoints}%
+ \let\next@incr@pageof = \relax
+ \fi
+ \next@incr@pageof
+}% increment@pageof@pagepoints
+\def\increment@pageof@pagebonuspoints{%
+ \addtocounter{pageof@pagebonuspoints}{1}%
+ \ifnum \theCurr@Page > \thepageof@pagebonuspoints\relax
+ \immediate\write\@mainaux
+ {\string\gdef\string\bonuspointsonpage@
+ \romannumeral \csname c@pageof@pagebonuspoints\endcsname{0}}%
+ % See if this has changed from the last run of LaTeX:
+ \@ifundefined{bonuspointsonpage@\romannumeral
+ \csname c@pageof@pagebonuspoints\endcsname}
+ {\global\@pointschangedtrue}%
+ {%
+ % OK; it's defined. See if it's changed:
+ \begingroup
+ \set@hlfcntr{tmp@hlfcntr}{\csname bonuspointsonpage@\romannumeral
+ \csname c@pageof@pagebonuspoints\endcsname\endcsname}%
+ \edef\othpt@check{\prtaux@hlfcntr{tmp@hlfcntr}}%
+ \def\pt@check{0}%
+ \ifx \pt@check \othpt@check
+ % Do nothing
+ \else
+ \global\@pointschangedtrue
+ \fi
+ \endgroup
+ }%
+ \let\next@incr@pageof = \increment@pageof@pagebonuspoints
+ \else
+ \copy@hlfcntr{@pagebonuspoints}{latest@bonuspoints}%
+ \set@hlfcntr{latest@bonuspoints}{0}%
+ % \page@withbonuspoints will be used to find the last
+ % page that has bonus points, which will be written to
+ % the .aux file via \AtEndDocument:
+ \global\edef\page@withbonuspoints{\thepageof@pagebonuspoints}%
+ \let\next@incr@pageof = \relax
+ \fi
+ \next@incr@pageof
+}% increment@pageof@pagebonuspoints
+
+
+
+
+
+
+\def\@checkqueslevel#1{%
+ \begingroup
+ \def\exam@temp{#1}%
+ \ifx\exam@temp\@queslevel
+ % Everything's fine; do nothing.
+ \else
+ \ClassError{exam}{%
+ I found a #1 where I expected to find a
+ \@queslevel\MessageBreak
+ }{%
+ Both #1 and \@queslevel \space can be used only inside the
+ correct \MessageBreak \space \space
+ environment and outside of any smaller environment
+ \MessageBreak
+ }%
+ \fi
+ \endgroup
+}
+
+\def\@doitem{\@ifnextchar[{\@readpoints}%
+ {\item@points@pageinfo}%
+}
+
+\def\@readpoints[#1]{%
+% We use \def for \@points instead of \edef because we don't want
+% \half (if present) to be expanded yet, so that the command \points
+% can figure out how to deal with it:
+ \def\@points{#1}%
+ \global \@placepointstrue
+ \if@addpoints
+ \if@bonus
+ \addto@hlfcntr{numbonuspoints}{\@points}%
+ \addto@hlfcntr{bonuspointsof@thisquestion}{\@points}%
+ % latest@bonuspoints is a holding area for bonus points to be
+ % added to @pagepoints after we check whether they're
+ % on the same page as the points currently counted
+ % by @pagepoints:
+ \addto@hlfcntr{latest@bonuspoints}{\@points}%
+ \else
+ \addto@hlfcntr{numpoints}{\@points}%
+ \addto@hlfcntr{pointsof@thisquestion}{\@points}%
+ % latest@points is a holding area for points to be
+ % added to @pagepoints after we check whether they're
+ % on the same page as the points currently counted
+ % by @pagepoints:
+ \addto@hlfcntr{latest@points}{\@points}%
+ \fi
+ \fi
+ \item@points@pageinfo
+}
+
+
+
+
+% Bug fix, 5 April 2004: \item@points@pageinfo
+% Appending \point@toks and \pageinfo@commands to \everypar:
+% Instead of appending the contents of \point@toks and
+% \pageinfo@commands to \everypar using \append@toklist,
+% we instead want to append only the two tokens
+% \the\point@toks
+% and the two tokens
+% \the\pageinfo@commands
+% to \everypar. We need to do this because if a questions environment
+% immediately follows a \section command, then @nobreak will be true,
+% and so the \if@nobreak inside of \everypar will *not* execute the
+% \everypar={} that we had been counting on to keep the points from
+% being inserted a second time in the second paragraph of a question.
+% Since we've put the command \global \point@toks={} inside of
+% \point@toks and the command \pageinfo@commands={} inside of
+% \pageinfo@commands, when the contents of \point@toks and of
+% \pageinfo@commands are executed (when we enter horizontal mode and
+% \everypar is dumped in), the contents of \point@toks and
+% \pageinfo@commands will be made empty, and so if
+% the second paragraph also gets \the\point@toks and
+% \the\pageinfo@commands, it won't matter.
+
+\def\item@points@pageinfo{%
+ \item
+ % 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
+ % command ``\sbox\@tempboxa{\makelabel{#1}}%''), expanding the
+ % argument of \qformat as it does so. Thus, @placepoints will be
+ % true when the argument of \qformat is expanded, and so if the
+ % 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).
+ %
+ % 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
+ % when \point@block is expanded so that \thepoints will actually
+ % print something. (After setting up \point@toks, we do
+ % \@placepointsfalse, but \point@block isn't actually expanded
+ % until we enter horizontal mode.) Thus, we define
+ % \padded@point@block, and use that instead of \point@block. We
+ % put \begingroup and \endgroup around this to confine the
+ % effect of \@placepointstrue and also to confine the effect of
+ % any declarations like, e.g., \bfseries that the user might put
+ % in the argument of a \pointformat command.
+ %
+ % Note: We first tried using an \edef to expand \point@block right
+ % here, while @placepoints is true, but that causes problems if
+ % the user puts a \boldmath declaration in the argument of a
+ % \pointformat command. Apparently, expanding \boldmath (without
+ % executing anything) gives you bunches of undefined control
+ % sequence errors.
+ \if@bonus
+ \def\padded@point@block{%
+ \begingroup
+ \@placepointstrue
+ \bonuspoint@block
+ \endgroup
+ }%
+ \else
+ \def\padded@point@block{%
+ \begingroup
+ \@placepointstrue
+ \point@block
+ \endgroup
+ }%
+ \fi
+ % \setup@point@toks puts commands into \point@toks to place
+ % \padded@point@block at the correct spot. It doesn't append
+ % anything to \everypar (we do that in this macro, below).
+ %
+ % If @qformat is true, and if we're currently doing a question (or
+ % if @bonusqformat is true and we're doing a bonusquestion)
+ % (rather than a part, subpart, or subsubpart), then we don't want
+ % to set the points (if any), since the points of a question will
+ % appear only if the user chooses to cause that by putting a
+ % \thepoints in the argument of the \qformat command.
+ \if@pointsdropped
+ % Do nothing!
+ \else
+ \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 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.218$\beta$, 2007/10/31 changes:
+ % Instead of appending
+ % \the \pageinfo@commands \the \point@toks
+ % to \everypar, we insert them into the box \@labels. This corrects
+ % the problem that arose when a question (or part, etc.) begins with
+ % a list environment (including verbatim, flushleft, center,
+ % flushright, and possibly others that are implemented as trivlist
+ % environments). The \item command in those environments throws
+ % away the previous contents of \everypar, and so the tokens \the
+ % \pageinfo@commands \the \point@toks didn't get inserted where we
+ % expected. List environments *do* preserve the contents of the box
+ % \@labels, though.
+ \global\setbox\@labels\hbox{\unhbox\@labels
+ \the \pageinfo@commands
+ \the \point@toks}%
+ % \edef\append@everypar{\noexpand\everypar={\the\everypar
+ % \noexpand\the \noexpand\pageinfo@commands
+ % \noexpand\the \noexpand\point@toks}}%
+ % \append@everypar
+}
+
+
+
+
+
+
+
+% Initialize \@points:
+% (The only reason I think this is necessary is in case the user uses
+% a \qformat command, puts \themarginpoints into the format (which is
+% *not* the intended use of \themarginpoints), and then doesn't have
+% any points for the first question.)
+\def\@points{0}
+
+
+\def\setup@point@toks{%
+% We set the token list \point@toks equal to the sequence of commands
+% needed to put \padded@point@block at the correct location, followed
+% by the tokens ``\global \point@toks={}''. The \question, \part,
+% \subpart, or \subsubpart command then adds the two tokens
+% ``\the\point@toks'' to \everypar.
+%
+% Note: It is not the *contents* of \point@toks that is added to
+% \everypar; just the two tokens ``\the\point@toks''. This difference
+% is the bug fix of 2 April 2004, described above (the bug was that in
+% earlier versions, we used to append the contents).
+%
+% The result of this is that whenever we finally enter horizontal mode
+% (because we finally encountered the text of a question, part,
+% subpart, or subsubpart) the contents of \point@toks will be dumped
+% into horizontal mode and executed, and so the points will be placed
+% and the token list \point@toks will be set to empty. Thus, in the
+% occasional circumstances in which \everypar is *not* set to empty
+% after being added to the first paragraph (which occurs when a
+% questions environment immediately follows a \section command), and
+% so \everypar will still contain ``\the\point@toks'' when it
+% encounters a possible second paragraph of the first question, the
+% tokens ``\the\point@toks'' will insert an *empty* token list, which
+% will do no harm.
+%
+ \if@pointstwosided
+ % Set \csname \q@label \endcsname equal to the thing
+ % that expands to the page number of the current (question or
+ % part or subpart or subsubpar; whatever it is), but do it
+ % carefully because, if we don't yet have page info, then it won't
+ % be defined:
+ \ifx\@queslevel\ques@ref
+ \def\q@label{Pg@question@\arabic{question}}
+ \else
+ \ifx\@queslevel\part@ref
+ \def\q@label{Pg@part@\arabic{question}@\arabic{partno}}
+ \else
+ \ifx\@queslevel\subpart@ref
+ \def\q@label{Pg@subpart@\arabic{question}%
+ @\arabic{partno}@\arabic{subpart}}
+ \else
+ \ifx\@queslevel\subsubpart@ref
+ \def\q@label{Pg@subsubpart@\arabic{question}%
+ @\arabic{partno}@\arabic{subpart}@\arabic{subsubpart}}
+ \else
+ \ClassError{exam}{%
+ This can't happen in function \protect\setup@point@toks
+ \MessageBreak
+ }{%
+ An unexplained error occurred in exam.cls;\MessageBreak
+ please inform the package maintainer, and send along
+ \MessageBreak
+ the LaTeX file that shows the error.\MessageBreak
+ }%
+ \fi
+ \fi
+ \fi
+ \fi
+ %
+ \expandafter\ifx \csname \q@label \endcsname\relax
+ % No page info yet; put it into the right margin
+ \@pointsinrightmargintrue
+ \@pointsinleftmarginfalse
+ \else
+ \ifodd \csname \q@label \endcsname\relax
+ \if@pointsinoutsidemargin
+ \@pointsinrightmargintrue
+ \@pointsinleftmarginfalse
+ \else
+ \@pointsinrightmarginfalse
+ \@pointsinleftmargintrue
+ \fi
+ \else
+ \if@pointsinoutsidemargin
+ \@pointsinrightmarginfalse
+ \@pointsinleftmargintrue
+ \else
+ \@pointsinrightmargintrue
+ \@pointsinleftmarginfalse
+ \fi
+ \fi
+ \fi
+ \fi
+ % That ends the \if@pointstwosided.
+ % Now we actually setup \point@toks:
+ \if@pointsinleftmargin
+ \point@toks={%
+ \llap{\padded@point@block
+ \hskip\@totalleftmargin
+ \hskip\marginpointssep
+ }%
+ \global \point@toks={}%
+ }%
+ \else
+ \if@pointsinrightmargin
+ \point@toks={%
+ \rlap{\hskip-\@totalleftmargin
+ \hskip\textwidth
+ \hskip\@rightmargin
+ \hskip-\rightpointsmargin
+ \llap{\padded@point@block}%
+ }%
+ \global \point@toks={}%
+ }%
+ \else
+ % The points just go after the question number:
+ \point@toks={%
+ \padded@point@block
+ \enspace
+ \global \point@toks={}%
+ }%
+ \fi
+ \fi
+}% setup@point@toks
+
+\def\droppoints{%
+ \leavevmode\unskip\nobreak\hfill
+ \rlap{\hskip\rightmargin % Defined by the list environment
+ \hskip\@rightmargin % Defined by exam.cls
+ \hskip-\rightpointsmargin
+ \llap{\padded@point@block}%
+ }% rlap
+ \par
+}
+
+\def\droptotalpoints{%
+ \leavevmode\unskip\nobreak\hfill
+ \rlap{\hskip\rightmargin % Defined by the list environment
+ \hskip\@rightmargin % Defined by exam.cls
+ \hskip-\rightpointsmargin
+ \llap{\total@block}%
+ }% rlap
+ \par
+}% droptotalpoints
+\def\droptotalbonuspoints{%
+ \leavevmode\unskip\nobreak\hfill
+ \rlap{\hskip\rightmargin % Defined by the list environment
+ \hskip\@rightmargin % Defined by exam.cls
+ \hskip-\rightpointsmargin
+ \llap{\bonustotal@block}%
+ }% rlap
+ \par
+}% droptotalbonuspoints
+
+% The following is the default definition;
+% it can be changed by a \totalformat command.
+\def\total@block{%
+ Total for Question \thequestion: \totalpoints\@marginpointname
+}% total@block
+\def\bonustotal@block{%
+ Total for Question \thequestion: \totalbonuspoints\@marginbonuspointname
+}% bonustotal@block
+
+\def\totalformat#1{%
+ \gdef\total@block{\begingroup #1\endgroup}%
+}% totalformat
+\def\bonustotalformat#1{%
+ \gdef\bonustotal@block{\begingroup #1\endgroup}%
+}% bonustotalformat
+% The following is for use in the argument to a \totalformat command:
+\def\totalpoints{\pointsofquestion{\arabic{question}}}
+\def\totalbonuspoints{\bonuspointsofquestion{\arabic{question}}}
+
+
+
+% @placepoints is set true when we encounter a question (or part, etc.)
+% that has points. It is set to false (1) when we set \point@toks equal
+% to the sequence of commands required to put the properly formatted
+% points onto the page (this happens only if @qformat is false or if
+% @qformat is true but we're not doing a question), or (2) by a
+% \question command or entering a parts environment (since if we're
+% doing a question and @qformat is true, we need to leave @placepoints
+% true so that the \thepoints command can tell if it should expand to
+% points or to nothing, and encountering a \question command or parts
+% environment tells us that we no longer have to deal with a possible
+% \thepoints, since we won't be expanding a qformat).
+\newif\if@placepoints
+\@placepointsfalse
+
+
+% \marginpointssep will be used if the user says
+% \pointsinleftmargin. It will be the distance from whatever encloses
+% the points (parentheses, brackets, or a box) to the left margin:
+\newlength\marginpointssep
+\setlength{\marginpointssep}{5pt}
+
+% \rightpointsmargin will be used if the user says \pointsinrightmargin.
+% It will be the distance from whatever encloses the point (parentheses,
+% brackets, or a box) to the right edge of the paper:
+\newlength\rightpointsmargin
+\setlength{\rightpointsmargin}{1cm}
+
+
+\newif\if@pointsdropped
+\newif\if@pointsinleftmargin
+\newif\if@pointsinrightmargin
+\newif\if@pointstwosided
+\newif\if@pointsinoutsidemargin
+
+% If we have \@pointstwosidedtrue and \@pointsinoutsidemarginfalse,
+% then the points will be printed on the inside margin (left on odd
+% numbered pages, right on even numbered pages). If we have
+% \@pointstwosidedfalse, then \if@pointsinoutsidemargin is ignored.
+
+% If we have \@pointstwosidedtrue, then both \@pointsinleftmargin and
+% \@pointsinrightmargin will be flipped back and forth, as needed, in
+% \setup@point@toks.
+
+\def\pointsinleftmargin{\global\@pointsinleftmargintrue
+ \global\@pointsinrightmarginfalse
+ \global\@pointsdroppedfalse
+ \global\@pointstwosidedfalse
+ \gdef\pt@name{\@marginpointname}%
+ \gdef\bnspt@name{\@marginbonuspointname}}
+\def\pointsinrightmargin{\global\@pointsinrightmargintrue
+ \global\@pointsinleftmarginfalse
+ \global\@pointsdroppedfalse
+ \global\@pointstwosidedfalse
+ \gdef\pt@name{\@marginpointname}%
+ \gdef\bnspt@name{\@marginbonuspointname}}
+\def\nopointsinmargin{\global\@pointsinleftmarginfalse
+ \global\@pointsinrightmarginfalse
+ \global\@pointsdroppedfalse
+ \global\@pointstwosidedfalse
+ \gdef\pt@name{\@pointname}%
+ \gdef\bnspt@name{\@bonuspointname}}
+\def\pointsdroppedatright{\global\@pointsdroppedtrue
+ \global\@pointsinleftmarginfalse
+ \global\@pointsinrightmarginfalse
+ \global\@pointstwosidedfalse
+ \gdef\pt@name{\@marginpointname}%
+ \gdef\bnspt@name{\@marginbonuspointname}}
+\def\pointstwosided{\global\@pointstwosidedtrue
+ \global\@pointsinoutsidemargintrue
+ \global\@pointsdroppedfalse
+ \gdef\pt@name{\@marginpointname}%
+ \gdef\bnspt@name{\@marginbonuspointname}}
+\def\pointstwosidedreversed{\global\@pointstwosidedtrue
+ \global\@pointsinoutsidemarginfalse
+ \global\@pointsdroppedfalse
+ \gdef\pt@name{\@marginpointname}%
+ \gdef\bnspt@name{\@marginbonuspointname}}
+\let\pointsinmargin=\pointsinleftmargin
+\let\nopointsinrightmargin=\nopointsinmargin
+\let\nopointsinleftmargin=\nopointsinmargin
+
+\nopointsinmargin
+
+
+% Will the points be displayed inside parentheses (the default), or
+% will they be boxed or bracketed, or customized using pointformat:
+\def\boxedpoints{%
+ \gdef\point@block{\fbox{\@points\pt@name}}%
+ \gdef\bonuspoint@block{\fbox{\@points\bnspt@name}}%
+}
+\def\noboxedpoints{%
+ \gdef\point@block{(\@points\pt@name)}%
+ \gdef\bonuspoint@block{(\@points\bnspt@name)}%
+}
+\def\bracketedpoints{%
+ \gdef\point@block{[\@points\pt@name]}%
+ \gdef\bonuspoint@block{[\@points\bnspt@name]}%
+}
+\let\nobracketedpoints=\noboxedpoints
+
+\def\pointformat#1{%
+ % We don't have to worry about the user putting things
+ % like \bfseries, etc. into \point@block, because
+ % \padded@point@block encloses \point@block in a group,
+ % which confines the effects of anything here:
+ \gdef\point@block{#1}%
+}
+
+\def\bonuspointformat#1{%
+ % We don't have to worry about the user putting things
+ % like \bfseries, etc. into \point@block, because
+ % \padded@point@block encloses \point@block in a group,
+ % which confines the effects of anything here:
+ \gdef\bonuspoint@block{#1}%
+}
+
+%Initialize:
+\noboxedpoints
+
+
+
+
+\def\pointname#1{\gdef\@pointname{#1}}
+\def\bonuspointname#1{\gdef\@bonuspointname{#1}}
+% Initialize to leave a space, and then the word `points':
+%%\pointname{ points}
+% The following improvement was contributed by
+% Mate Wierdl <mw@wierdlmpc.msci.memphis.edu>
+% If the number of points is ``1'', then the default value of
+% \pointname will print `` point'' instead of `` points'' (and this
+% version of the command doesn't generate an error message if the
+% points entry is something other than a number):
+% Note the space before the \points in the following; it's
+% intentional!)
+\pointname{ \points}
+\bonuspointname{ \bonuspoints}
+\newcommand\point@sing{point}
+\newcommand\point@plur{points}
+\newcommand\pointpoints[2]{%
+ \renewcommand\point@sing{#1}%
+ \renewcommand\point@plur{#2}%
+}
+%\newcommand\bonuspoint@sing{bonus point}
+%\newcommand\bonuspoint@plur{bonus points}
+\newcommand\bonuspoint@sing{point (bonus)}
+\newcommand\bonuspoint@plur{points (bonus)}
+\newcommand\bonuspointpoints[2]{%
+ \renewcommand\bonuspoint@sing{#1}%
+ \renewcommand\bonuspoint@plur{#2}%
+}
+
+
+
+% The command \points:
+% We use \ifthenelse and \equal so that if the user types something
+% other than a legit point value, there still won't be any error
+% messages. We rig it so that if the point value looks like it's
+% intended to be One or one or ONE, or some strange way of attempting
+% one half, then it will expand to the singular value. Alas, this is
+% only useful for English, but I'm hoping few or no users will try doing
+% this anyway.
+
+% 0 points, one half point, 1 point, 1 and a half points, etc.:
+\newcommand\points{%
+ \begingroup
+ \let\half=\relax
+ \edef\pt@string{\@points}%
+ \ifthenelse{\equal{\pt@string}{1} \or \equal{\pt@string}{\half}}
+ {\point@sing}{\point@plur}%
+ \endgroup
+}% \points
+\newcommand\bonuspoints{%
+ \begingroup
+ \let\half=\relax
+ \edef\pt@string{\@points}%
+ \ifthenelse{\equal{\pt@string}{1} \or \equal{\pt@string}{\half}}
+ {\bonuspoint@sing}{\bonuspoint@plur}%
+ \endgroup
+}% \bonuspoints
+%\newcommand\points{%
+% \begingroup
+% \let\half=\relax
+% \edef\pt@string{\@points}%
+% \ifthenelse{\equal{\pt@string}{1} \or \equal{\pt@string}{\half} \or
+% \equal{\pt@string}{0\half} \or \equal{\pt@string}{0 \half}
+% \equal{\pt@string}{one} \or \equal{\pt@string}{One} \or
+% \equal{\pt@string}{ONE}}
+% {\point@sing}{\point@plur}%
+% \endgroup
+%}
+
+%\newcommand\points{\ifthenelse{\equal{\@points}{1}}{\point@sing}{\point@plur}}
+% If we used the following line instead, then you'd get an error
+% message if the point value contained something other than a valid
+% integer:
+%\pointname{ \ifthenelse{\@points = 1}{point}{points}}
+
+% We used to define a command named \marks that works like \points,
+% except that it expands to either ``mark'' or ``marks'', but that
+% conflicts with some package or other. Thus, we'll implement
+% \marksnotpoints using the \pointpoints command instead:
+\newcommand\marksnotpoints{%
+ \pointpoints{mark}{marks}%
+ \bonuspointpoints{mark (bonus)}{marks (bonus)}%
+}% \marksnotpoints
+
+
+% \@marginpointname is used in place of \@pointname if any of
+% \@pointsinmargin, \@pointsinrightmargin, and \@pointsdropped are
+% true:
+\def\marginpointname#1{\gdef\@marginpointname{#1}}
+\marginpointname{}
+\def\marginbonuspointname#1{\gdef\@marginbonuspointname{#1}}
+\marginbonuspointname{ (bonus)}
+
+
+%--------------------------------------------------------------------
+% choices (for multiple choice) and checkboxes
+
+
+\renewcommand\thechoice{\Alph{choice}}
+\newcommand\choicelabel{\thechoice.}
+
+% We will have \@correctchoicetrue when we're printing solutions
+% and we're printing the correct choice of a choices or
+% oneparchoices environment.
+% We'll say \begingroup before saying \@correctchoicetrue
+% and we'll say \endgroup at either the next \choice or \correctchoice
+% or the end of the choices or oneparchoices environment.
+% Thus, we'll never again need to say \@correctchoicefalse
+\newif\if@correctchoice
+\@correctchoicefalse
+
+\newcommand\CorrectChoiceEmphasis[1]{%
+ \def\CorrectChoice@Emphasis{#1}%
+}
+\CorrectChoiceEmphasis{\bfseries}
+\let\correctchoiceemphasis\CorrectChoiceEmphasis
+
+% Note: \do@choice@pageinfo is used in both the choices and
+% the checkboxes environments.
+\newtoks\choice@toks
+\def\do@choice@pageinfo{%
+ \choice@toks={%
+ \questionobject@pluspagecheck
+ \choice@toks={}%
+ }%
+ % Version 2.217-beta changes:
+ % Instead of appending stuff to \everypar, we insert
+ % \the \pageinfo@commands and \the \point@toks
+ % into the box \@labels:
+ \global\setbox\@labels\hbox{\unhbox\@labels
+ \the \choice@toks}%
+ % \edef\append@everypar{\noexpand\everypar={\the\everypar
+ % \noexpand\the \noexpand\choice@toks}}%
+ % \append@everypar
+}% do@choice@pageinfo
+
+
+
+% Added 22 April 2004: Increased the \leftmargin by 2.5em,
+% so the choices will be visibly indented.
+\newenvironment{choices}%
+ {\list{\choicelabel}%
+ {\usecounter{choice}\def\makelabel##1{\hss\llap{##1}}%
+ \settowidth{\leftmargin}{W.\hskip\labelsep\hskip 2.5em}%
+ \def\choice{%
+ \if@correctchoice
+ \color@endgroup
+ \endgroup
+ \fi
+ \item
+ \do@choice@pageinfo
+ } % choice
+ \def\CorrectChoice{%
+ \if@correctchoice
+ \color@endgroup
+ \endgroup
+ \fi
+ \ifprintanswers
+ % We can't say \choice here, because that would
+ % insert an \endgroup:
+ % 2016/05/10: We say \color@begingroup in addition to
+ % \begingroup in case \CorrectChoiceEmphasis involves color
+ % and the text exactly fills the line (which would
+ % otherwise create a blank line after this choice):
+ % 2016/05/11: We leave hmode if we're in it,
+ % i.e., if there's no blank line preceding this
+ % \CorrectChoice command. (Without this, the
+ % \special created by a \color{whatever} command that might
+ % be inserted by \CorrectChoice@Emphasis would be appended
+ % to the previous \choice, which could cause an extra
+ % (blank) line to be inserted before this \CorrectChoice.)
+ % Since \par and \endgraf seem to cancel \@totalleftmargin
+ % (for reasons I don't understand), we'll do the following:
+ % Motivated by the def of \leavevmode,
+ % \def\leavevmode{\unhbox\voidb@x}
+ % we will now leave hmode (if we're in hmode):
+ \ifhmode \unskip\unskip\unvbox\voidb@x \fi
+ \begingroup \color@begingroup \@correctchoicetrue
+ \CorrectChoice@Emphasis
+ \fi
+ \item
+ \do@choice@pageinfo
+ } % CorrectChoice
+ \let\correctchoice\CorrectChoice
+ \labelwidth\leftmargin\advance\labelwidth-\labelsep
+ \topsep=0pt
+ \partopsep=0pt
+ \choiceshook
+ }%
+ }%
+ {\if@correctchoice \color@endgroup \endgroup \fi \endlist}
+
+\newenvironment{oneparchoices}%
+ {%
+ \setcounter{choice}{0}%
+ \def\choice{%
+ \if@correctchoice \endgroup \fi
+ \refstepcounter{choice}%
+ \ifnum\value{choice}>1\relax
+ \penalty -50\hskip 1em plus 1em\relax
+ \fi
+ \choicelabel
+ % No need to put the following into a token string; we just put
+ % the choicelabel onto the page, so we're at the spot whose page
+ % number we want to record:
+ \questionobject@pluspagecheck
+ \nobreak\enskip
+ }% choice
+ \def\CorrectChoice{%
+ \if@correctchoice \endgroup \fi
+ \refstepcounter{choice}%
+ \ifprintanswers
+ \begingroup \@correctchoicetrue
+ \CorrectChoice@Emphasis
+ \fi
+ \ifnum\value{choice}>1\relax
+ \penalty -50\hskip 1em plus 1em\relax
+ \fi
+ \choicelabel
+ % No need to put the following into a token string; we just put
+ % the choicelabel onto the page, so we're at the spot whose page
+ % number we want to record:
+ \questionobject@pluspagecheck
+ \nobreak\enskip
+ }% CorrectChoice
+ \let\correctchoice\CorrectChoice
+ \let\par\@empty
+ % If we're continuing the paragraph containing the question,
+ % then leave a bit of space before the first choice:
+ \ifvmode\else\enskip\fi
+ \ignorespaces
+ }%
+ {\if@correctchoice \endgroup \fi}
+
+\newcommand{\checkboxchar}[1]{\def\checkbox@char{#1}}
+\newcommand{\checkedchar}[1]{\def\checked@char{#1}}
+\checkboxchar{$\bigcirc$}
+\checkedchar{$\surd$}
+
+\newenvironment{checkboxes}%
+ {\list{\checkbox@char}%
+ {%
+ \settowidth{\leftmargin}{W.\hskip\labelsep\hskip 2.5em}%
+ \def\choice{%
+ \if@correctchoice
+ \color@endgroup \endgroup
+ \fi
+ \item
+ \do@choice@pageinfo
+ } % choice
+ \def\CorrectChoice{%
+ \if@correctchoice
+ \color@endgroup \endgroup
+ \fi
+ \ifprintanswers
+ % We can't say \choice here, because that would
+ % insert an \endgroup.
+ % 2016/05/10: We say \color@begingroup in addition to
+ % \begingroup in case \CorrectChoiceEmphasis involves color
+ % and the text exactly fills the line (which would
+ % otherwise create a blank line after this choice):
+ % 2016/05/11: We leave hmode if we're in it,
+ % i.e., if there's no blank line preceding this
+ % \CorrectChoice command. (Without this, the
+ % \special created by a \color{whatever} command that might
+ % be inserted by \CorrectChoice@Emphasis would be appended
+ % to the previous \choice, which could cause an extra
+ % (blank) line to be inserted before this \CorrectChoice.)
+ % Since \par and \endgraf seem to cancel \@totalleftmargin
+ % (for reasons I don't understand), we'll do the following:
+ % Motivated by the def of \leavevmode,
+ % \def\leavevmode{\unhbox\voidb@x}
+ % we will now leave hmode (if we're in hmode):
+ \ifhmode \unskip\unskip\unvbox\voidb@x \fi
+ \begingroup \color@begingroup \@correctchoicetrue
+ \CorrectChoice@Emphasis
+ \item[\checked@char]
+ \else
+ \item
+ \fi
+ \do@choice@pageinfo
+ } % CorrectChoice
+ \let\correctchoice\CorrectChoice
+ \labelwidth\leftmargin\advance\labelwidth-\labelsep
+ \topsep=0pt
+ \partopsep=0pt
+ \checkboxeshook
+ }%
+ }%
+ {\if@correctchoice \color@endgroup \endgroup \fi \endlist}
+
+\newenvironment{oneparcheckboxes}%
+ {%
+ % Although we're not printing numbers for the choices, we use the
+ % choice counter to keep track of whether a choice is the first
+ % one (in which case we don't leave any additional space) or a
+ % later one (in which case we do leave additional space):
+ \setcounter{choice}{0}%
+ \def\choice{%
+ \if@correctchoice \endgroup \fi
+ \stepcounter{choice}%
+ \ifnum\value{choice}>1\relax
+ \penalty -50\hskip 1em plus 1em\relax
+ \fi
+ \checkbox@char
+ % No need to put the following into a token string; we just put
+ % \checkbox@char onto the page, so we're at the spot whose page
+ % number we want to record:
+ \questionobject@pluspagecheck
+ \nobreak\enskip
+ }% choice
+ \def\CorrectChoice{%
+ \if@correctchoice \endgroup \fi
+ \stepcounter{choice}%
+ \ifprintanswers
+ \begingroup \@correctchoicetrue
+ \CorrectChoice@Emphasis
+ \fi
+ \ifnum\value{choice}>1\relax
+ \penalty -50\hskip 1em plus 1em\relax
+ \fi
+ \ifprintanswers
+ \checked@char
+ \else
+ \checkbox@char
+ \fi
+ % No need to put the following into a token string; we just put
+ % the choicelabel onto the page, so we're at the spot whose page
+ % number we want to record:
+ \questionobject@pluspagecheck
+ \nobreak\enskip
+ }% CorrectChoice
+ \let\correctchoice\CorrectChoice
+ \let\par\@empty
+ % If we're continuing the paragraph containing the question,
+ % then leave a bit of space before the first choice:
+ \ifvmode\else\enskip\fi
+ \ignorespaces
+ }%
+ {\if@correctchoice \endgroup \fi}
+
+
+%--------------------------------------------------------------------
+% Answer Lines (for short answer questions)
+
+% Note: \ques@ref is also used in \item@points@pageinfo, and all four
+% of the following are used in \setup@point@toks
+
+\def\ques@ref{question}
+\def\part@ref{part}
+\def\subpart@ref{subpart}
+\def\subsubpart@ref{subsubpart}
+
+% Note: \answerclearance is also used by \fillin
+
+\newlength\answerlinelength
+\newlength\answerskip
+\newlength\answerclearance
+\setlength\answerlinelength{1in}
+\setlength\answerskip{2ex}
+\setlength\answerclearance{0.2ex}
+
+\newcommand\answerline[1][{}]{%
+ % One optional argument, the default value of which is empty.
+ \ifx\@queslevel\ques@ref
+ \let\ans@l=\questionlabel
+ \else
+ \ifx\@queslevel\part@ref
+ \let\ans@l=\partlabel
+ \else
+ \ifx\@queslevel\subpart@ref
+ \let\ans@l=\subpartlabel
+ \else
+ \ifx\@queslevel\subsubpart@ref
+ \let\ans@l=\subsubpartlabel
+ \else
+ % Oops; no question level defined.
+ % We must be outide of the questions environment.
+ % Just leave out the label, I guess:
+ \def\ans@l{}%
+ \fi
+ \fi
+ \fi
+ \fi
+ \par \nobreak \vskip \answerskip
+ \hfill
+ \ifprintanswers
+ \ans@l~\hbox to 0pt{\hbox to \answerlinelength{\hrulefill}\hss}%
+ \raise \answerclearance\hbox to \answerlinelength{%
+ % 2016/05/10: Added \color@begingroup and \color@endgroup:
+ \color@begingroup
+ \CorrectChoice@Emphasis \hfil #1\hss
+ \color@endgroup}%
+ \else
+ \ans@l~\hbox to \answerlinelength{\hrulefill}%
+ \fi
+ \par
+}% answerline
+
+%--------------------------------------------------------------------
+% \fillin, for fill-in-the-blank questions
+
+\newlength\fillinlinelength
+\setlength\fillinlinelength{1in}
+
+% \fillin can take two optional arguments.
+%
+% The first optional argument is the answer to be printed above the line
+% when \printanswers is in effect; the default value is empty. That
+% line is printed a distance of \answerclearance below the baseline.
+%
+% The second optional argument is the length of the line that we print;
+% the default value is \fillinlinelength. The value of
+% \fillinlinelength is set with the command
+%
+% \setlength\fillinlinelength{1in}
+%
+% and can be changed by giving a new \setlength command.
+%
+% When answers are being printed, the optional argument is printed
+% subject to the declarations in the argument of the last
+% \CorrectChoiceEmphasis command. It is centered on the line unless it
+% is too long, in which case it extends to the right of the line.
+%
+% \fillin eats (and ignores) space characters appearing before the
+% first optional argument. It also eats (and ignores) space
+% characters appearing after the first optional argument and before
+% the second optional argument. However, if exactly one optional
+% argument appears, and if there are one or more space characters
+% following that one optional argument, then those spaces are replaced
+% by a single space character, but not eaten.
+
+\newcommand\fillin[1][{}]{%
+ \def\fillin@ans{#1}%
+ \fillin@relay
+}% fillin
+
+\newcommand\fillin@relay{%
+ % We use \exam@ifnextchar, a variation on \@ifnextchar.
+ % If \exam@ifnextchar encounters one or more space characters
+ % followed by a [, then those spaces are ignored (just as they would
+ % be by \@ifnextchar). However, if one or more space characters are
+ % followed by a non-space character other than [, then
+ % \exam@ifnextchar inserts a space following the
+ % {\@fillin@relay[\fillinlinelength]} that is the third argument to
+ % \exam@ifnextchar.
+ \exam@ifnextchar[{\@fillin@relay}
+ {\@fillin@relay[\fillinlinelength]}%
+}% fillin@relay
+
+\def\@fillin@relay[#1]{%
+ % The first argument is in \fillin@ans, the second is #1.
+ \leavevmode
+ \ifprintanswers
+ \rlap{\raise -\answerclearance \hbox to #1{\hrulefill}}%
+ \begingroup
+ \setbox0 \hbox{\color@begingroup
+ \CorrectChoice@Emphasis \fillin@ans \color@endgroup}%
+ \ifdim\wd0 > #1\relax
+ \hbox{\color@begingroup\CorrectChoice@Emphasis \fillin@ans
+ \color@endgroup}%
+ \else
+ \hbox to #1{\color@begingroup\CorrectChoice@Emphasis
+ \hfil \fillin@ans \hfil\color@endgroup}%
+ \fi
+ \endgroup
+ \else
+ \raise -\answerclearance \hbox to #1{\hrulefill}%
+ \fi
+}% @fillin@relay
+
+% \exam@ifnextchar is used by \fillin.
+% \exam@ifnextchar is a variation of \@ifnextchar that does not always
+% ignore space tokens. If \exam@ifnextchar encounters one or more
+% space tokens, it makes note of that (with the command
+% \@tempswatrue). If the first non-space character encountered
+% matches argument #1, then any spaces that had been encountered are
+% ignored. However, if one or more spaces are encountered and the
+% first non-space character found does not match argument #1, then
+% \exam@ifnextchar produces argument #3 followed by a space character.
+% (This differs from the behavior of \new@ifnextchar in amsgen.sty,
+% which does lookahead for any character, including a space.) This
+% code (as well as the idea for it) is due to Dan Luecking and Ulrich
+% Diez.
+\long\def\exam@ifnextchar#1#2#3{%
+ \let\reserved@d=#1%
+ \def\reserved@a{#2}%
+ \def\reserved@b{#3}%
+ % The following says we haven't yet seen any spaces:
+ \@tempswafalse
+ \futurelet\@let@token\exam@ifnch
+}% exam@ifnextchar
+
+\def\exam@ifnch{%
+ \ifx\@let@token\@sptoken
+ % Signal that we've found a space:
+ \@tempswatrue
+ \let\reserved@c\exam@xifnch % this gobbles the space
+ \else
+ \ifx\@let@token\reserved@d
+ \let\reserved@c\reserved@a
+ \else
+ \if@tempswa
+ \def\reserved@c{\expandafter\reserved@b\space}%
+ \else
+ \let\reserved@c\reserved@b
+ \fi
+ \fi
+ \fi
+ \reserved@c
+}% exam@ifnch
+
+% The following defines \exam@xifnch so that it will eat a space
+% following it and then call \exam@ifnch:
+{% keep redefinition of \: local
+ \def\:{\exam@xifnch}
+ \expandafter\gdef\: {\futurelet\@let@token\exam@ifnch}
+}
+
+%--------------------------------------------------------------------
+% \fillwithlines
+
+
+% \fillwithlines takes one argument, which is either a length or \fill
+% or \stretch{number}, and it fills that much vertical space with
+% horizontal lines that run the length of the current line. That is,
+% they extend from the current left margin (which depends on whether
+% we're in a question, part, subpart, or subsubpart) to the right
+% margin.
+%
+% The distance between the lines is \linefillheight, whose default value
+% is set with the command
+%
+% \setlength\linefillheight{.25in}
+%
+% This value can be changed by giving a new \setlength command.
+%
+% The thickness of the lines is \linefillthickness, whose default value
+% is set with the command
+%
+% \setlength\linefillthickness{.1pt}
+%
+% This value can be changed by giving a new \setlength command.
+%
+% As of version 2.503, 2016/03/25, the lines drawn by the
+% \fillwithlines command will be drawn in color if the user has given
+% the command
+%
+% \colorfillwithlines.
+%
+% The actual drawing of the lines is now done by the command
+% \do@fillwithlines, after the \fillwithlines command decides whether
+% they will be in color. The default color is set by the command
+%
+% \definecolor{FillWithLinesColor}{gray}{0.8}
+%
+% and the color can be changed by giving a new \definecolor command.
+% You can return to black lines by giving the command
+%
+% \nocolorfillwithlines
+
+\newlength\linefillheight
+\newlength\linefillthickness
+\setlength\linefillheight{.25in}
+\setlength\linefillthickness{0.1pt}
+
+
+
+
+\newif\if@colorfillwithlines
+\@colorfillwithlinesfalse
+\def\colorfillwithlines{%
+ \@ifundefined{definecolor}
+ {%
+ \ClassError{exam}{%
+ You must load the color package with the command\MessageBreak
+ \space\space\protect\usepackage{color}\MessageBreak
+ in order to use the command \protect\colorfillwithlines
+ \MessageBreak
+ }{%
+ This command makes use of the package color.sty,\MessageBreak
+ and so you have to load color.sty before your\MessageBreak
+ \protect\begin{document} command.\MessageBreak
+ }%
+ }%
+ {%
+ \definecolor{FillWithLinesColor}{gray}{0.8}
+ \@colorfillwithlinestrue
+ }%
+}% \colorfillwithlines
+\def\nocolorfillwithlines{\@colorfillwithlinesfalse}
+
+\newcommand\fillwithlines[1]{%
+ \if@colorfillwithlines
+ \color@begingroup
+ \color{FillWithLinesColor}%
+ \do@fillwithlines{#1}%
+ \color@endgroup
+ \else
+ \do@fillwithlines{#1}%
+ \fi
+}% \fillwithlines
+
+\newcommand\linefill{\leavevmode
+ \leaders\hrule height \linefillthickness \hfill\kern\z@}
+
+% \do@fillwithlines is called only by \fillwithlines
+\def\do@fillwithlines#1{%
+ \begingroup
+ \ifhmode
+ \par
+ \fi
+ \hrule height \z@
+ \nobreak
+ \setbox0=\hbox to \hsize{\hskip \@totalleftmargin
+ \vrule height \linefillheight depth \z@ width \z@
+ \linefill}%
+ % We use \cleaders (rather than \leaders) so that a given
+ % vertical space will always produce the same number of lines
+ % no matter where on the page it happens to start:
+ \cleaders \copy0 \vskip #1 \hbox{}%
+ \endgroup
+}% \do@fillwithlines
+
+%--------------------------------------------------------------------
+% \fillwithdottedlines
+
+
+% \fillwithdottedlines is similar to \fillwithlines, except that it
+% fills the space with dotted lines (created by \dotfill) rather than
+% with solid lines.
+
+% \fillwithdottedlines takes one argument, which is either a length or
+% \fill or \stretch{number}, and it fills that much vertical space
+% with dotted lines that run the length of the current line. That is,
+% they extend from the current left margin (which depends on whether
+% we're in a question, part, subpart, or subsubpart) to the right
+% margin.
+%
+% The distance between the lines is \dottedlinefillheight, whose
+% default value is set with the command
+%
+% \setlength\dottedlinefillheight{.25in}
+%
+% This value can be changed by giving a new \setlength command.
+
+% As of version 2.503, 2016/03/25, the dotted lines drawn by the
+% \fillwithdottedlines command will be drawn in color if the user has
+% given the command
+%
+% \colorfillwithdottedlines.
+%
+% The actual drawing of the lines is now done by the command
+% \do@fillwithdottedlines, after the \fillwithdottedlines command
+% decides whether they will be in color. The default color is set by
+% the command
+%
+% \definecolor{FillWithDottedLinesColor}{gray}{0.8}
+%
+% and the color can be changed by giving a new \definecolor command.
+% You can return to black lines by giving the command
+%
+% \nocolorfillwithdottedlines
+
+
+\newlength\dottedlinefillheight
+\setlength\dottedlinefillheight{.25in}
+
+\newif\if@colorfillwithdottedlines
+\@colorfillwithdottedlinesfalse
+\def\colorfillwithdottedlines{%
+ \@ifundefined{definecolor}
+ {%
+ \ClassError{exam}{%
+ You must load the color package with the command\MessageBreak
+ \space\space\protect\usepackage{color}\MessageBreak
+ in order to use the command \protect\colorfillwithdottedlines
+ \MessageBreak
+ }{%
+ This command makes use of the package color.sty,\MessageBreak
+ and so you have to load color.sty before your\MessageBreak
+ \protect\begin{document} command.\MessageBreak
+ }%
+ }%
+ {%
+ \definecolor{FillWithDottedLinesColor}{gray}{0.8}
+ \@colorfillwithdottedlinestrue
+ }%
+}% \colorfillwithdottedlines
+\def\nocolorfillwithdottedlines{\@colorfillwithdottedlinesfalse}
+
+\newcommand\fillwithdottedlines[1]{%
+ \if@colorfillwithdottedlines
+ \color@begingroup
+ \color{FillWithDottedLinesColor}%
+ \do@fillwithdottedlines{#1}%
+ \color@endgroup
+ \else
+ \do@fillwithdottedlines{#1}%
+ \fi
+}% \fillwithdottedlines
+
+% \do@fillwithdottedlines is called only by \fillwithdottedlines
+\def\do@fillwithdottedlines#1{%
+ \begingroup
+ \ifhmode
+ \par
+ \fi
+ \hrule height \z@
+ \nobreak
+ \setbox0=\hbox to \hsize{\hskip \@totalleftmargin
+ \vrule height \dottedlinefillheight depth \z@ width \z@
+ \dotfill}%
+ % We use \cleaders (rather than \leaders) so that a given
+ % vertical space will always produce the same number of lines
+ % no matter where on the page it happens to start:
+ \cleaders \copy0 \vskip #1 \hbox{}%
+ \endgroup
+}% \do@fillwithdottedlines
+
+%--------------------------------------------------------------------
+% \fillwithgrid
+
+
+% \fillwithgrid is similar to \fillwithlines, except that it
+% fills the space with a grid.
+
+% \fillwithgrid takes one argument, which is either a length or \fill
+% or \stretch{number}, and it fills that much vertical space with a
+% grid that runs the length of the current line. That is, it extends
+% from the current left margin (which depends on whether we're in a
+% question, part, subpart, or subsubpart) to the right margin.
+%
+% The default grid size and grid line thickness were set by the
+% commands
+%
+% \setlength{\gridsize}{5mm}
+% \setlength{\gridlinewidth}{0.1pt}
+%
+% You can change either or both of those by giving new \setlength
+% commands. The period of the grid is \gridsize (both horizontally
+% and vertically). That is, the horizontal distance from the left
+% edge of one vertical line to the left edge of the next vertical line
+% is \gridsize, as is the vertical distance from the top edge of one
+% horizontal line to the top edge of the next horizontal line. Thus,
+% each square has outer side length equal to \gridsize+\gridlinewidth.
+
+% By default, the created grids are in black. However, if you give the
+% commands
+%
+% \usepackage{color}
+% \colorgrids
+%
+% then the grids will be in color, by default a light gray. That
+% default color was defined by the command
+%
+% \definecolor{GridColor}{gray}{0.8}
+%
+% You can change the color by redefining the color GridColor by giving
+% a new \definecolor command.
+
+\newif\if@colorgrids
+\newcommand\colorgrids{%
+ \@ifundefined{definecolor}
+ {%
+ \ClassError{exam}{%
+ You must load the color package with the command\MessageBreak
+ \space\space\protect\usepackage{color}\MessageBreak
+ in order to use the command \protect\colorgrids
+ }{%
+ This command makes use of the package color.sty,\MessageBreak
+ and so you have to load color.sty before your\MessageBreak
+ \protect\begin{document} command.\MessageBreak
+ }%
+ }%
+ {%
+ \definecolor{GridColor}{gray}{0.8}
+ \@colorgridstrue
+ }%
+}% \colorgrids
+\newcommand\nocolorgrids{\@colorgridsfalse}
+\nocolorgrids
+
+\newlength\gridsize
+\newlength\gridlinewidth
+\setlength{\gridsize}{5mm}
+\setlength{\gridlinewidth}{0.1pt}
+
+\def\fillwithgrid#1{%
+ \begingroup
+ \ifhmode
+ \par
+ \fi
+ \hrule height \z@
+ \nobreak
+
+ % We first set box0 equal to an \hbox which, when printed, is a
+ % square with width and height equal to \gridsize+\gridlinewidth,
+ % but which has
+ % width equal to \gridsize,
+ % height equal to \gridsize, and
+ % depth equal to 0pt.
+ % When we put multiple copies of it together using \leaders or
+ % \cleaders, the right edge will coincide with the left edge of the
+ % next box and the bottom edge will coincide with the top edge of
+ % the box below it.
+ \setlength{\@tempdima}{\gridsize}
+ \addtolength{\@tempdima}{\gridlinewidth}
+ \setlength{\@tempdimb}{\gridsize}
+ \addtolength{\@tempdimb}{-\gridlinewidth}
+ \setbox0=\hbox{%
+ \rlap{\vrule height \gridsize depth \gridlinewidth width \gridlinewidth}%
+ \rlap{\vrule height \gridsize depth -\@tempdimb width \@tempdima}%
+ \vrule height 0pt depth \gridlinewidth width \@tempdima
+ \llap{\vrule height \gridsize depth \gridlinewidth width \gridlinewidth}%
+ }%
+ \wd0=\gridsize
+ \dp0=0pt
+ % Now we set box1 equal to an \hbox containing a single line of
+ % copies of box0. We use \leaders (instead of \cleaders) so that
+ % if we use it twice on a page, once with a question and once
+ % with a part, the boxes will line up vertically. We add a kern of
+ % \gridlinewidth at the right because the rightmost vertical line
+ % appears to the right of where the \leaders command thinks that it
+ % appears.
+ \setbox1=\hbox to \textwidth{%
+ \color@begingroup
+ \if@colorgrids
+ \color{GridColor}%
+ \fi
+ \hskip \@totalleftmargin \leaders\copy0\hfil \kern\gridlinewidth
+ \color@endgroup
+ }%
+ % Finally: We create the grid, using \cleaders: We use \cleaders
+ % (rather than \leaders) so that a given vertical space will always
+ % produce the same number of lines no matter where on the page it
+ % happens to start. We add a kern of \gridlinewidth because the
+ % bottommost horizontal line appears below where the \cleaders
+ % command thinks that it appears.
+ \cleaders \copy1 \vskip #1 \kern \gridlinewidth \hbox{}%
+ \endgroup
+}% fillwithgrid
+
+%--------------------------------------------------------------------
+% \makeemptybox
+
+% \makeemptybox takes one argument, which is a length, and it creates
+% an empty box of width the length of the current line and of height
+% equal to the argument. That is, the box extends from the current
+% left margin (which depends on whether we're in a question, part,
+% subpart, or subsubpart) to the right margin.
+
+% As of version 2.304, the argument of \makeemptybox can be either
+% a length, or \fill, or \stretch{number}.
+
+% \newcommand\makeemptybox[1]{
+% \par
+% \begingroup
+% \setlength{\fboxsep}{0pt}%
+% \framebox[\linewidth]{%
+% \vrule height 0pt depth #1 width 0pt
+% }%
+% \endgroup
+% }
+
+\newlength\minboxheight
+\setlength\minboxheight{.1in}
+
+
+% As of version 2.502, 2016/03/23, the frame drawn by the
+% \makeemptybox command will be drawn in color if the user has given
+% the command \colorsolutionboxes. The actual drawing of the box is
+% now done by the command \do@emptybox, after the \makeemptybox
+% command decides whether it will be in color.
+
+\newcommand\makeemptybox[1]{%
+ \if@colorsolutionboxes
+ \color@begingroup
+ \color{SolutionBoxColor}%
+ \do@emptybox{#1}%
+ \color@endgroup
+ \else
+ \do@emptybox{#1}%
+ \fi
+}
+
+% The command \do@emptybox is called only by \makeemptybox.
+\newcommand\do@emptybox[1]{%
+ \par
+ \hbox to \hsize{\hskip\@totalleftmargin \leaders\hrule\hfill}%
+ \nointerlineskip
+ \begingroup
+ \setbox0=\hbox to \hsize{\hskip\@totalleftmargin
+ \vrule height\minboxheight \hfill \vrule}%
+ % The vertical size desired may not be an exact multiple of
+ % \minboxheight, and so \cleaders might leave a gap between the
+ % vertical lines and the horizontal lines above and below it.
+ % Thus, we put a single copy of \box0 immediately below the
+ % horizontal line above and we'll also put a single copy of \box0
+ % immediately above the horizontal line below.
+ \copy0
+ \nobreak
+ \vskip -\minboxheight
+ \cleaders \copy0 \vskip #1
+ \vskip -\minboxheight
+ \nointerlineskip
+ \copy0
+ \endgroup
+ \nointerlineskip
+ \hbox to \hsize{\hskip\@totalleftmargin \leaders\hrule\hfill}%
+}
+
+%--------------------------------------------------------------------
+% \uplevel and \fullwidth
+% 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
+% directions about how that question should be answered, you would
+% say \uplevel{Answer this question correctly.}
+%
+% \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
+ % is in the argument of the \uplevel command:
+ \leftskip=\@totalleftmargin
+ \advance\leftskip-\leftmargin
+ % 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
+ #1%
+ }% vbox
+ \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
+ #1%
+ }% vbox
+ \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}
+
+
+%--------------------------------------------------------------------
+%--------------------------------------------------------------------
+%
+% ********************
+% ** GRADING TABLES **
+% ********************
+
+
+
+\newcounter{@iterator}
+\newlength\@cellwidth
+
+\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}}
+
+
+% The following are the versions for bonusgradetable:
+\def\bhqword#1{\def\@bhqword{#1}}
+\def\bhpword#1{\def\@bhpword{#1}}
+\def\bhsword#1{\def\@bhsword{#1}}
+\def\bhtword#1{\def\@bhtword{#1}}
+\def\bhpgword#1{\def\@bhpgword{#1}}
+
+\def\bvqword#1{\def\@bvqword{#1}}
+\def\bvpword#1{\def\@bvpword{#1}}
+\def\bvsword#1{\def\@bvsword{#1}}
+\def\bvtword#1{\def\@bvtword{#1}}
+\def\bvpgword#1{\def\@bvpgword{#1}}
+
+% The following are the versions for combinedgradetable:
+\def\chqword#1{\def\@chqword{#1}}
+\def\chpword#1{\def\@chpword{#1}}
+\def\chbpword#1{\def\@chbpword{#1}}
+\def\chsword#1{\def\@chsword{#1}}
+\def\chtword#1{\def\@chtword{#1}}
+\def\chpgword#1{\def\@chpgword{#1}}
+
+\def\cvqword#1{\def\@cvqword{#1}}
+\def\cvpword#1{\def\@cvpword{#1}}
+\def\cvbpword#1{\def\@cvbpword{#1}}
+\def\cvsword#1{\def\@cvsword{#1}}
+\def\cvtword#1{\def\@cvtword{#1}}
+\def\cvpgword#1{\def\@cvpgword{#1}}
+
+
+
+% Initialize:
+\cellwidth{2em}
+\gradetablestretch{1.5}
+
+\hqword{Question:}
+\hpgword{Page:}
+\hpword{Points:}
+\hsword{Score:}
+\htword{Total}
+\vpword{Points}
+\vsword{Score}
+\vtword{Total:}
+\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:}
+
+\chqword{Question:}
+\chpgword{Page:}
+\chpword{Points:}
+\chbpword{Bonus Points:}
+\chsword{Score:}
+\chtword{Total}
+\cvqword{Question}
+\cvpgword{Page}
+\cvpword{Points}
+\cvbpword{Bonus Points}
+\cvsword{Score}
+\cvtword{Total:}
+
+% Before we created multirow and multicolumn tables, he only commands
+% here accessible to the user were \gradetable, \bonusgradetable,
+% \combinedgradetable, \pointtable, \bonuspointtable,
+% \combinedpointtable, \partialgradetable,
+% \partialbonusgradetable, \partialcombinedtable, \partialpointtable,
+% \partialbonuspointtable, \partialcombinedpointtable,
+% \begingradingrange, \endgradingrange, \pointsinrange,
+% \bonuspointsinrange, \firstqinrange, \lastqinrange, and
+% \numqinrange. The new user commands are
+%
+% \def\multirowgradetable
+% \def\multirowpointtable
+% \def\multirowbonusgradetable
+% \def\multirowbonuspointtable
+% \def\multirowcombinedgradetable
+% \def\multirowcombinedpointtable
+%
+% \def\multirowpartialgradetable
+% \def\multirowpartialpointtable
+% \def\multirowpartialbonusgradetable
+% \def\multirowpartialbonuspointtable
+% \def\multirowpartialcombinedgradetable
+% \def\multirowpartialcombinedpointtable
+%
+% \def\multicolumngradetable
+% \def\multicolumnpointtable
+% \def\multicolumnbonusgradetable
+% \def\multicolumnbonuspointtable
+% \def\multicolumncombinedgradetable
+% \def\multicolumncombinedpointtable
+%
+% \def\multicolumnpartialgradetable
+% \def\multicolumnpartialpointtable
+% \def\multicolumnpartialbonusgradetable
+% \def\multicolumnpartialbonuspointtable
+% \def\multicolumnpartialcombinedgradetable
+% \def\multicolumnpartialcombinedpointtable
+
+
+% The possibilities are
+
+% \gradetable[v][questions]
+% \gradetable[v][pages]
+% \gradetable[h][questions]
+% \gradetable[h][pages]
+
+% \bonusgradetable[v][questions]
+% \bonusgradetable[v][pages]
+% \bonusgradetable[h][questions]
+% \bonusgradetable[h][pages]
+
+% \combinedgradetable[v][questions]
+% \combinedgradetable[v][pages]
+% \combinedgradetable[h][questions]
+% \combinedgradetable[h][pages]
+
+% \pointtable[v][questions]
+% \pointtable[v][pages]
+% \pointtable[h][questions]
+% \pointtable[h][pages]
+
+% \bonuspointtable[v][questions]
+% \bonuspointtable[v][pages]
+% \bonuspointtable[h][questions]
+% \bonuspointtable[h][pages]
+
+% \combinedpointtable[v][questions]
+% \combinedpointtable[v][pages]
+% \combinedpointtable[h][questions]
+% \combinedpointtable[h][pages]
+
+% \partialgradetable{whatever}[v][questions]
+% \partialgradetable{whatever}[v][pages]
+% \partialgradetable{whatever}[h][questions]
+% \partialgradetable{whatever}[h][pages]
+
+% \partialbonusgradetable{whatever}[v][questions]
+% \partialbonusgradetable{whatever}[v][pages]
+% \partialbonusgradetable{whatever}[h][questions]
+% \partialbonusgradetable{whatever}[h][pages]
+
+% \partialcombinedgradetable{whatever}[v][questions]
+% \partialcombinedgradetable{whatever}[v][pages]
+% \partialcombinedgradetable{whatever}[h][questions]
+% \partialcombinedgradetable{whatever}[h][pages]
+
+% \partialpointtable{whatever}[v][questions]
+% \partialpointtable{whatever}[v][pages]
+% \partialpointtable{whatever}[h][questions]
+% \partialpointtable{whatever}[h][pages]
+
+% \partialbonuspointtable{whatever}[v][questions]
+% \partialbonuspointtable{whatever}[v][pages]
+% \partialbonuspointtable{whatever}[h][questions]
+% \partialbonuspointtable{whatever}[h][pages]
+
+% \partialcombinedpointtable{whatever}[v][questions]
+% \partialcombinedpointtable{whatever}[v][pages]
+% \partialcombinedpointtable{whatever}[h][questions]
+% \partialcombinedpointtable{whatever}[h][pages]
+
+% \begingradingrange{whatever}
+% \endgradingrange{whatever}
+%
+% \pointsinrange{whatever}
+% \bonuspointsinrange{whatever}
+%
+% \firstqinrange{whatever}
+% \lastqinrange{whatever}
+% \numqinrange{whatever}
+%
+% where ``whatever'' is a label chosen by the user.
+%
+% \def\multirowgradetable{numcols}[questions or pages]
+% \def\multirowpointtable{numcols}[questions or pages]
+% \def\multirowbonusgradetable{numcols}[questions or pages]
+% \def\multirowbonuspointtable{numcols}[questions or pages]
+% \def\multirowcombinedgradetable{numcols}[questions or pages]
+% \def\multirowcombinedpointtable{numcols}[questions or pages]
+%
+% \def\multirowpartialgradetable{numcols}{rangename}[questions or pages]
+% \def\multirowpartialpointtable{numcols}{rangename}[questions or pages]
+% \def\multirowpartialbonusgradetable{numcols}{rangename}[questions or pages]
+% \def\multirowpartialbonuspointtable{numcols}{rangename}[questions or pages]
+% \def\multirowpartialcombinedgradetable{numcols}{rangename}[questions or pages]
+% \def\multirowpartialcombinedpointtable{numcols}{rangename}[questions or pages]
+%
+% \def\multicolumngradetable{numrows}[questions or pages]
+% \def\multicolumnpointtable{numrows}[questions or pages]
+% \def\multicolumnbonusgradetable{numrows}[questions or pages]
+% \def\multicolumnbonuspointtable{numrows}[questions or pages]
+% \def\multicolumncombinedgradetable{numrows}[questions or pages]
+% \def\multicolumncombinedpointtable{numrows}[questions or pages]
+
+% \def\multicolumnpartialgradetable{numrows}{rangename}[questions or pages]
+% \def\multicolumnpartialpointtable{numrows}{rangename}[questions or pages]
+% \def\multicolumnpartialbonusgradetable{numrows}{rangename}[questions or pages]
+% \def\multicolumnpartialbonuspointtable{numrows}{rangename}[questions or pages]
+% \def\multicolumnpartialcombinedgradetable{numrows}{rangename}[questions or pages]
+% \def\multicolumnpartialcombinedpointtable{numrows}{rangename}[questions or pages]
+
+
+% If one or both optional arguments are omitted, the defaults are
+% `[v]' and `[questions]'.
+
+% \@scorestrue means we're doing \gradetable
+% \@scoresfalse mans we're doing \pointtable
+\newif\if@scores
+
+% \@partialtrue means we're doing \partialgradetable,
+% \partialbonusgradetable, \partialcombinedgradetable,
+% \partialpointtable, \partialbonuspointtable, or
+% \partialcombinedpointtable:
+\newif\if@partial
+
+% \@combinedtrue means we're doing \combinedgradetable,
+% \combinedpointtable, \partialcombinedgradetable, or
+% \partialcombinedpointtable:
+\newif\if@combined
+
+% It's OK to use the counter num@cols as a scratch counter
+% in \begingradingrange and \endgradingrange because
+% it's only used in typesetting tables:
+\def\begingradingrange#1{%
+ \setcounter{num@cols}{\value{question}}%
+ \addtocounter{num@cols}{1}%
+ \immediate\write\@mainaux
+ {\string\expandafter\string\gdef
+ \string\csname\space range@#1@firstq\string\endcsname
+ {\arabic{num@cols}}}%
+ \write\@mainaux
+ {\string\expandafter\string\gdef
+ \string\csname\space range@#1@firstp\string\endcsname
+ {\thepage}}%
+}% begingradingrange
+
+\def\endgradingrange#1{%
+ \setcounter{num@cols}{\value{question}}%
+ \immediate\write\@mainaux
+ {\string\expandafter\string\gdef
+ \string\csname\space range@#1@lastq\string\endcsname
+ {\arabic{num@cols}}}%
+ \write\@mainaux
+ {\string\expandafter\string\gdef
+ \string\csname\space range@#1@lastp\string\endcsname
+ {\thepage}}%
+}% endgradingrange
+
+
+% Now that grading tables may be for only part of the exam,
+% we need the counter tbl@points to add up the total points
+% for the questions (or pages) that appear on the table:
+\new@hlfcntr{tbl@points}
+
+% We'll use the counter tbl@bonuspoints to add up the total bonus
+% points for the questions (or pages) that appear on the table:
+\new@hlfcntr{tbl@bonuspoints}
+
+%--------------------------------------------------------------------
+% multirow tables, non-partial:
+
+\def\multirowgradetable#1{%
+ \@scorestrue
+ \@bonusfalse
+ \@partialfalse
+ \@combinedfalse
+ \setcounter{num@rows}{#1}%
+ \i@gtable[h]%
+}
+
+\def\multirowpointtable#1{%
+ \@scoresfalse
+ \@bonusfalse
+ \@partialfalse
+ \@combinedfalse
+ \setcounter{num@rows}{#1}%
+ \i@gtable[h]%
+}
+
+\def\multirowbonusgradetable#1{%
+ \@scorestrue
+ \@bonustrue
+ \@partialfalse
+ \@combinedfalse
+ \setcounter{num@rows}{#1}%
+ \i@gtable[h]%
+}
+
+\def\multirowbonuspointtable#1{%
+ \@scoresfalse
+ \@bonustrue
+ \@partialfalse
+ \@combinedfalse
+ \setcounter{num@rows}{#1}%
+ \i@gtable[h]%
+}
+
+\def\multirowcombinedgradetable#1{%
+ \@scorestrue
+ \@bonusfalse
+ \@partialfalse
+ \@combinedtrue
+ \setcounter{num@rows}{#1}%
+ \i@gtable[h]%
+}
+
+\def\multirowcombinedpointtable#1{%
+ \@scoresfalse
+ \@bonusfalse
+ \@partialfalse
+ \@combinedtrue
+ \setcounter{num@rows}{#1}%
+ \i@gtable[h]%
+}
+
+%--------------------------------------------------------------------
+% multirow tables, partial:
+
+\def\multirowpartialgradetable#1#2{%
+ \@scorestrue
+ \@bonusfalse
+ \@partialtrue
+ \@combinedfalse
+ \def\tbl@range{#2}%
+ \setcounter{num@rows}{#1}%
+ \i@gtable[h]%
+}
+
+\def\multirowpartialpointtable#1#2{%
+ \@scoresfalse
+ \@bonusfalse
+ \@partialtrue
+ \@combinedfalse
+ \def\tbl@range{#2}%
+ \setcounter{num@rows}{#1}%
+ \i@gtable[h]%
+}
+
+\def\multirowpartialbonusgradetable#1#2{%
+ \@scorestrue
+ \@bonustrue
+ \@partialtrue
+ \@combinedfalse
+ \def\tbl@range{#2}%
+ \setcounter{num@rows}{#1}%
+ \i@gtable[h]%
+}
+
+\def\multirowpartialbonuspointtable#1#2{%
+ \@scoresfalse
+ \@bonustrue
+ \@partialtrue
+ \@combinedfalse
+ \def\tbl@range{#2}%
+ \setcounter{num@rows}{#1}%
+ \i@gtable[h]%
+}
+
+\def\multirowpartialcombinedgradetable#1#2{%
+ \@scorestrue
+ \@bonusfalse
+ \@partialtrue
+ \@combinedtrue
+ \def\tbl@range{#2}%
+ \setcounter{num@rows}{#1}%
+ \i@gtable[h]%
+}
+
+\def\multirowpartialcombinedpointtable#1#2{%
+ \@scoresfalse
+ \@bonusfalse
+ \@partialtrue
+ \@combinedtrue
+ \def\tbl@range{#2}%
+ \setcounter{num@rows}{#1}%
+ \i@gtable[h]%
+}
+
+%--------------------------------------------------------------------
+% multicolumn tables, non-partial:
+
+\def\multicolumngradetable#1{%
+ \@scorestrue
+ \@bonusfalse
+ \@partialfalse
+ \@combinedfalse
+ \setcounter{num@cols}{#1}%
+ \i@gtable[v]%
+}
+
+\def\multicolumnpointtable#1{%
+ \@scoresfalse
+ \@bonusfalse
+ \@partialfalse
+ \@combinedfalse
+ \setcounter{num@cols}{#1}%
+ \i@gtable[v]%
+}
+
+\def\multicolumnbonusgradetable#1{%
+ \@scorestrue
+ \@bonustrue
+ \@partialfalse
+ \@combinedfalse
+ \setcounter{num@cols}{#1}%
+ \i@gtable[v]%
+}
+
+\def\multicolumnbonuspointtable#1{%
+ \@scoresfalse
+ \@bonustrue
+ \@partialfalse
+ \@combinedfalse
+ \setcounter{num@cols}{#1}%
+ \i@gtable[v]%
+}
+
+\def\multicolumncombinedgradetable#1{%
+ \@scorestrue
+ \@bonusfalse
+ \@partialfalse
+ \@combinedtrue
+ \setcounter{num@cols}{#1}%
+ \i@gtable[v]%
+}
+
+\def\multicolumncombinedpointtable#1{%
+ \@scoresfalse
+ \@bonusfalse
+ \@partialfalse
+ \@combinedtrue
+ \setcounter{num@cols}{#1}%
+ \i@gtable[v]%
+}
+
+%--------------------------------------------------------------------
+% multicolumn tables, partial:
+
+\def\multicolumnpartialgradetable#1#2{%
+ \@scorestrue
+ \@bonusfalse
+ \@partialtrue
+ \@combinedfalse
+ \def\tbl@range{#2}%
+ \setcounter{num@cols}{#1}%
+ \i@gtable[v]%
+}
+
+\def\multicolumnpartialpointtable#1#2{%
+ \@scoresfalse
+ \@bonusfalse
+ \@partialtrue
+ \@combinedfalse
+ \def\tbl@range{#2}%
+ \setcounter{num@cols}{#1}%
+ \i@gtable[v]%
+}
+
+\def\multicolumnpartialbonusgradetable#1#2{%
+ \@scorestrue
+ \@bonustrue
+ \@partialtrue
+ \@combinedfalse
+ \def\tbl@range{#2}%
+ \setcounter{num@cols}{#1}%
+ \i@gtable[v]%
+}
+
+\def\multicolumnpartialbonuspointtable#1#2{%
+ \@scoresfalse
+ \@bonustrue
+ \@partialtrue
+ \@combinedfalse
+ \def\tbl@range{#2}%
+ \setcounter{num@cols}{#1}%
+ \i@gtable[v]%
+}
+
+\def\multicolumnpartialcombinedgradetable#1#2{%
+ \@scorestrue
+ \@bonusfalse
+ \@partialtrue
+ \@combinedtrue
+ \def\tbl@range{#2}%
+ \setcounter{num@cols}{#1}%
+ \i@gtable[v]%
+}
+
+\def\multicolumnpartialcombinedpointtable#1#2{%
+ \@scoresfalse
+ \@bonusfalse
+ \@partialtrue
+ \@combinedtrue
+ \def\tbl@range{#2}%
+ \setcounter{num@cols}{#1}%
+ \i@gtable[v]%
+}
+
+%--------------------------------------------------------------------
+% partial single row (and column) tables:
+
+\def\partialgradetable#1{%
+ \@scorestrue
+ \@bonusfalse
+ \@partialtrue
+ \@combinedfalse
+ \def\tbl@range{#1}%
+ % We don't yet know if the table is vertical or horizontal, and so
+ % we don't know if we need to set num@cols or num@rows. We'll set
+ % them both, and we'll later on just ignore the value of the one
+ % that we didn't need to set here:
+ \setcounter{num@cols}{1}%
+ \setcounter{num@rows}{1}%
+ % If the user doesn't include the optional argument
+ % choosing between vertical and horizontal,
+ % we give them vertical:
+ \@ifnextchar[{\i@gtable}{\i@gtable[v]}%
+}% partialgradetable
+
+\def\partialbonusgradetable#1{%
+ \@scorestrue
+ \@bonustrue
+ \@partialtrue
+ \@combinedfalse
+ \def\tbl@range{#1}%
+ % We don't yet know if the table is vertical or horizontal, and so
+ % we don't know if we need to set num@cols or num@rows. We'll set
+ % them both, and we'll later on just ignore the value of the one
+ % that we didn't need to set here:
+ \setcounter{num@cols}{1}%
+ \setcounter{num@rows}{1}%
+ % If the user doesn't include the optional argument
+ % choosing between vertical and horizontal,
+ % we give them vertical:
+ \@ifnextchar[{\i@gtable}{\i@gtable[v]}%
+}% partialbonusgradetable
+
+\def\partialcombinedgradetable#1{%
+ \@scorestrue
+ \@bonusfalse
+ \@partialtrue
+ \@combinedtrue
+ \def\tbl@range{#1}%
+ % We don't yet know if the table is vertical or horizontal, and so
+ % we don't know if we need to set num@cols or num@rows. We'll set
+ % them both, and we'll later on just ignore the value of the one
+ % that we didn't need to set here:
+ \setcounter{num@cols}{1}%
+ \setcounter{num@rows}{1}%
+ % If the user doesn't include the optional argument
+ % choosing between vertical and horizontal,
+ % we give them vertical:
+ \@ifnextchar[{\i@gtable}{\i@gtable[v]}%
+}% partialcombinedgradetable
+
+\def\partialpointtable#1{%
+ \@scoresfalse
+ \@bonusfalse
+ \@partialtrue
+ \@combinedfalse
+ \def\tbl@range{#1}%
+ % We don't yet know if the table is vertical or horizontal, and so
+ % we don't know if we need to set num@cols or num@rows. We'll set
+ % them both, and we'll later on just ignore the value of the one
+ % that we didn't need to set here:
+ \setcounter{num@cols}{1}%
+ \setcounter{num@rows}{1}%
+ % If the user doesn't include the optional argument
+ % choosing between vertical and horizontal,
+ % we give them vertical:
+ \@ifnextchar[{\i@gtable}{\i@gtable[v]}%
+}% partialpointtable
+
+\def\partialbonuspointtable#1{%
+ \@scoresfalse
+ \@bonustrue
+ \@partialtrue
+ \@combinedfalse
+ \def\tbl@range{#1}%
+ % We don't yet know if the table is vertical or horizontal, and so
+ % we don't know if we need to set num@cols or num@rows. We'll set
+ % them both, and we'll later on just ignore the value of the one
+ % that we didn't need to set here:
+ \setcounter{num@cols}{1}%
+ \setcounter{num@rows}{1}%
+ % If the user doesn't include the optional argument
+ % choosing between vertical and horizontal,
+ % we give them vertical:
+ \@ifnextchar[{\i@gtable}{\i@gtable[v]}%
+}% partialbonuspointtable
+
+\def\partialcombinedpointtable#1{%
+ \@scoresfalse
+ \@bonusfalse
+ \@partialtrue
+ \@combinedtrue
+ \def\tbl@range{#1}%
+ % We don't yet know if the table is vertical or horizontal, and so
+ % we don't know if we need to set num@cols or num@rows. We'll set
+ % them both, and we'll later on just ignore the value of the one
+ % that we didn't need to set here:
+ \setcounter{num@cols}{1}%
+ \setcounter{num@rows}{1}%
+ % If the user doesn't include the optional argument
+ % choosing between vertical and horizontal,
+ % we give them vertical:
+ \@ifnextchar[{\i@gtable}{\i@gtable[v]}%
+}% partialcombinedpointtable
+
+%--------------------------------------------------------------------
+% single row (and column) tables, non-partial:
+
+\def\gradetable{%
+ \@scorestrue
+ \@bonusfalse
+ \@partialfalse
+ \@combinedfalse
+ % We don't yet know if the table is vertical or horizontal, and so
+ % we don't know if we need to set num@cols or num@rows. We'll set
+ % them both, and we'll later on just ignore the value of the one
+ % that we didn't need to set here:
+ \setcounter{num@cols}{1}%
+ \setcounter{num@rows}{1}%
+ % If the user doesn't include the optional argument
+ % choosing between vertical and horizontal,
+ % we give them vertical:
+ \@ifnextchar[{\i@gtable}{\i@gtable[v]}%
+}% gradetable
+
+\def\bonusgradetable{%
+ \@scorestrue
+ \@bonustrue
+ \@partialfalse
+ \@combinedfalse
+ % We don't yet know if the table is vertical or horizontal, and so
+ % we don't know if we need to set num@cols or num@rows. We'll set
+ % them both, and we'll later on just ignore the value of the one
+ % that we didn't need to set here:
+ \setcounter{num@cols}{1}%
+ \setcounter{num@rows}{1}%
+ % If the user doesn't include the optional argument
+ % choosing between vertical and horizontal,
+ % we give them vertical:
+ \@ifnextchar[{\i@gtable}{\i@gtable[v]}%
+}% bonusgradetable
+
+\def\combinedgradetable{%
+ \@scorestrue
+ \@bonusfalse
+ \@partialfalse
+ \@combinedtrue
+ % We don't yet know if the table is vertical or horizontal, and so
+ % we don't know if we need to set num@cols or num@rows. We'll set
+ % them both, and we'll later on just ignore the value of the one
+ % that we didn't need to set here:
+ \setcounter{num@cols}{1}%
+ \setcounter{num@rows}{1}%
+ % If the user doesn't include the optional argument
+ % choosing between vertical and horizontal,
+ % we give them vertical:
+ \@ifnextchar[{\i@gtable}{\i@gtable[v]}%
+}% bonusgradetable
+
+\def\pointtable{%
+ \@scoresfalse
+ \@bonusfalse
+ \@partialfalse
+ \@combinedfalse
+ % We don't yet know if the table is vertical or horizontal, and so
+ % we don't know if we need to set num@cols or num@rows. We'll set
+ % them both, and we'll later on just ignore the value of the one
+ % that we didn't need to set here:
+ \setcounter{num@cols}{1}%
+ \setcounter{num@rows}{1}%
+ % If the user doesn't include the optional argument
+ % choosing between vertical and horizontal,
+ % we give them vertical:
+ \@ifnextchar[{\i@gtable}{\i@gtable[v]}%
+}% pointtable
+
+\def\bonuspointtable{%
+ \@scoresfalse
+ \@bonustrue
+ \@partialfalse
+ \@combinedfalse
+ % We don't yet know if the table is vertical or horizontal, and so
+ % we don't know if we need to set num@cols or num@rows. We'll set
+ % them both, and we'll later on just ignore the value of the one
+ % that we didn't need to set here:
+ \setcounter{num@cols}{1}%
+ \setcounter{num@rows}{1}%
+ % If the user doesn't include the optional argument
+ % choosing between vertical and horizontal,
+ % we give them vertical:
+ \@ifnextchar[{\i@gtable}{\i@gtable[v]}%
+}% bonuspointtable
+
+\def\combinedpointtable{%
+ \@scoresfalse
+ \@bonusfalse
+ \@partialfalse
+ \@combinedtrue
+ % We don't yet know if the table is vertical or horizontal, and so
+ % we don't know if we need to set num@cols or num@rows. We'll set
+ % them both, and we'll later on just ignore the value of the one
+ % that we didn't need to set here:
+ \setcounter{num@cols}{1}%
+ \setcounter{num@rows}{1}%
+ % If the user doesn't include the optional argument
+ % choosing between vertical and horizontal,
+ % we give them vertical:
+ \@ifnextchar[{\i@gtable}{\i@gtable[v]}%
+}% bonuspointtable
+
+%--------------------------------------------------------------------
+
+% \i@gtable and \ii@gtable insert any missing optional arguments
+% (the defaults being [v] and [questions]) and then make sure
+% that the user said \addpoints and that this isn't the
+% first run of LaTeX.
+% \find@p@or@q@range then branches, depending on whether the user
+% selected [questions] or [pages].
+
+\def\i@gtable[#1]{%
+ % If the user doesn't include the second optional argument,
+ % which chooses between questions and pages,
+ % we give them questions:
+ \@ifnextchar[{\ii@gtable{#1}}{\ii@gtable{#1}[questions]}%
+}
+\def\ii@gtable#1[#2]{%
+ % We get here from \i@gtable.
+ % We make sure the user said \addpoints, and then make sure
+ % that this isn't the first run of LaTeX (by checking that
+ % \exam@numpoints is defined). If both of those are OK,
+ % we go to \find@p@or@q@range to see whether we're doing a table
+ % indexed by questions or by pages.
+ \if@addpoints
+ \@ifundefined{exam@numpoints}%
+ {\ClassWarning{exam}%
+ {%
+ You must run LaTeX again to produce the
+ table.\MessageBreak
+ }%
+ \fbox{Run \LaTeX{} again to produce the table}%
+ }%
+ {\find@p@or@q@range{#1}{#2}}%
+ \else
+ \ClassError{exam}{%
+ You must give the command \protect\addpoints\MessageBreak
+ \space\space in order to create a grade table.\MessageBreak
+ }{%
+ If you don't give the command \protect\addpoints\MessageBreak
+ \space\space then we're not keeping track of point values.
+ \MessageBreak
+ }%
+ \fi
+}% ii@gtable
+
+\def\@questionsref{questions}
+\def\@pagesref{pages}
+\def\find@p@or@q@range#1#2{%
+ % We get here from \ii@gtable.
+ % The first argument should be ``v'' or ``h'';
+ % the second argument should be ``questions'' or ``pages''.
+ % See whether we're doing a table indexed by
+ % questions (in which case we go to \find@qrange) or by pages (in
+ % which case we go to \find@prange):
+ \begingroup
+ % We've begun a group that will contain the construction of the
+ % table, to confine the effect of any \def's that we use.
+ \def\exam@temp{#2}%
+ \ifx\exam@temp\@questionsref
+ \tbl@pgsfalse
+ \find@qrange{#1}%
+ \else
+ \ifx\exam@temp\@pagesref
+ \tbl@pgstrue
+ \find@prange{#1}%
+ \else
+ \ClassError{exam}{%
+ Grade and point tables can be indexed\MessageBreak
+ \space\space by either `questions' or `pages',\MessageBreak
+ \space\space but not by `#2'.\MessageBreak
+ }{%
+ Grade tables and point tables can be indexed by questions or
+ pages;\MessageBreak
+ \space\space for others, you're on your own.\MessageBreak
+ }%
+ \fbox{\textbf{Error:} grade or point table: Invalid argument
+ `#2' must be `questions' or `pages'.}%
+ \fi
+ \fi
+ \endgroup
+}% find@p@or@q@range
+
+% \range@undefined can be called from either \find@qrange or
+% \find@prange
+\def\range@undefined{%
+ \fbox{Warning: grading range `\tbl@range ' not defined;
+ run \LaTeX{} again.}%
+ \ClassWarning{exam}{%
+ Grading range `\tbl@range' not defined.\MessageBreak
+ \space\space Run LaTeX again to produce the table.\MessageBreak
+ }%
+}% range@undefined
+
+
+%--------------------------------------------------------------------
+%--------------------------------------------------------------------
+% Grade and point tables indexed by question numbers:
+
+% When we get to \find@qrange, we know we're doing a table indexed by
+% question numbers and that this is not the first run of latex. The
+% argument is either ``v'' or ``h''. If we're not doing a partial
+% table, then \find@qrange sets \tbl@firstq and \first@pq@index to 1
+% and \tbl@lastq and \last@pq@index to \numquestions. Otherwise,
+% \find@qrange makes sure the grading range is defined and that its
+% last question isn't before its first question. \find@qrange then
+% calls \tbl@v@or@h, passing along the argument that is either ``v''
+% or ``h''.
+
+\def\find@qrange#1{%
+ % We get here from \find@p@or@q@range.
+ % We're doing a table indexed by question numbers.
+ \if@partial
+ \@ifundefined{range@\tbl@range @firstq}%
+ {%
+ \range@undefined
+ }%
+ {%
+ \@ifundefined{range@\tbl@range @lastq}%
+ {%
+ \range@undefined
+ }%
+ {%
+ \edef\tbl@firstq{\csname range@\tbl@range @firstq\endcsname}%
+ \edef\tbl@lastq{\csname range@\tbl@range @lastq\endcsname}%
+ \let\first@pq@index=\tbl@firstq
+ \let\last@pq@index=\tbl@lastq
+ % Check that firstq precedes or equals lastq:
+ \ifnum \tbl@firstq > \tbl@lastq\relax
+ \fbox{\textbf{Error:} Grading Range `\tbl@range':
+ Last question precedes first question.}%
+ \ClassError{exam}{%
+ In grading range `\tbl@range',
+ the last question\MessageBreak
+ \space\space comes before the first question.\MessageBreak
+ }{%
+ \string\begingradingrange \space must precede
+ \string\endgradingrange \space by at
+ least one question.\MessageBreak
+ }%
+ \else
+ \tbl@v@or@h{#1}%
+ \fi
+ }%
+ }%
+ \else
+ \def\tbl@firstq{1}%
+ \let\first@pq@index=\tbl@firstq
+ % \numquestions is always defined, even if this is the first
+ % run of LaTeX and \exam@numquestions isn't defined.
+ % If it's the first run of LaTeX, then its value isn't useful,
+ % but it's never used until a later run (when its value is useful).
+ \def\tbl@lastq{\numquestions}%
+ \let\last@pq@index=\tbl@lastq
+ \tbl@v@or@h{#1}%
+ \fi
+}% find@qrange
+
+\def\@vref{v}
+\def\@href{h}
+\def\tbl@v@or@h#1{%
+ % \first@pq@index=\tbl@firstq or \tbl@firstp and
+ % \last@pq@index=\tbl@lastq or \tbl@lastp have already been set.
+ % The argument should be either `v' or `h', and we branch
+ % accordingly.
+ \def\exam@temp{#1}%
+ \ifx\exam@temp\@vref
+ \check@num@cols@v
+ \else
+ \ifx\exam@temp\@href
+ \check@num@rows@h
+ \else
+ \ClassError{exam}{%
+ Grade or point table: the argument `#1'\MessageBreak
+ \space\space must be `v' or `h'.
+ \MessageBreak
+ }{%
+ Grade tables and point tables can be either vertical or
+ horizontal;\MessageBreak
+ \space\space no diagonals allowed.\MessageBreak
+ }%
+ \fbox{\textbf{Error:} grade or point table: Invalid argument
+ `#1' must be `v' or `h'.}%
+ \fi
+ \fi
+}% tbl@v@or@h
+
+%--------------------------------------------------------------------
+%--------------------------------------------------------------------
+% Grade and point tables indexed by page numbers:
+
+
+% The only pages listed are those on which there are a nonzero number
+% of points. We check pages \tbl@firstp through \tbl@lastp
+% Once we've checked that, e.g., \lastpage@withpoints and
+% \pointsonpage@\romannumeral{\lastpage@withpoints} are defined, we
+% can safely (we think) check \pointsonpage@\romannumeral{n} for all n
+% between \tbl@firstp and \tbl@lastp without generating errors.
+%
+% Actually: Since we added the notion of half points and half counters
+% (a long time ago), there won't be any errors even if
+% \pointsonpage@\romannumeral{n} isn't defined, since it's tested by the
+% lines:
+% \set@hlfcntr{tmp@hlfcntr}{\csname pointsonpage@\romannumeral
+% \csname c@@iterator\endcsname\endcsname}%
+% \ifhlfcntr@pos{tmp@hlfcntr}%
+% and if
+% \csname pointsonpage@\romannumeral
+% \csname c@@iterator\endcsname\endcsname
+% isn't defined, tmp@hlfcntr gets the value zero (because of the way
+% that \set@hlfcntr is written).
+
+% \find@prange makes sure the grading range is defined and that its
+% last page isn't before its first page (if it's a partial table). In
+% any case, it then sets \tbl@firstp and \tbl@lastp, and calls
+% \check@secondrun.
+
+\def\find@prange#1{%
+ % We get here from \find@p@or@q@range.
+ % We're doing a table indexed by pages.
+ % The argument is either ``v'' or ``h''.
+ % We first determine the first and last page of the range, storing
+ % those in \first@pq@index=\tbl@firstp and
+ % \last@pq@index=\tbl@lastp. If not a partial table, we set
+ % \first@pq@index=\tbl@firstp to 1 and \last@pq@index=\tbl@lastp to
+ % the last page with the appropriate points (and so if it's a
+ % combined table, it's the last page to have either bonus or
+ % non-bonus points).
+ % We then call \check@secondrun, passing it the argument that we
+ % received (i.e., we say \check@secondrun{#1}) to make sure
+ % we've done at least two runs of latex (so that we'll have the
+ % information we need about which pages have points on them).
+ \if@partial
+ \@ifundefined{range@\tbl@range @firstp}%
+ {%
+ \range@undefined
+ }%
+ {%
+ \@ifundefined{range@\tbl@range @lastp}%
+ {%
+ \range@undefined
+ }%
+ {%
+ \edef\tbl@firstp{\csname range@\tbl@range @firstp\endcsname}%
+ \edef\tbl@lastp{\csname range@\tbl@range @lastp\endcsname}%
+ \let\first@pq@index=\tbl@firstp
+ \let\last@pq@index=\tbl@lastp
+ % Check that firstp precedes or equals lastp:
+ \ifnum \tbl@firstp > \tbl@lastp\relax
+ \fbox{\textbf{Error:} Grading Range `\tbl@range ':
+ Last page precedes first page.}%
+ \ClassError{exam}{%
+ In grading range `\tbl@range', the last page\MessageBreak
+ \space\space comes before the first page.\MessageBreak
+ }{%
+ \string\begingradingrange \space must precede
+ \string\endgradingrange.\MessageBreak
+ }%
+ \else
+ \check@secondrun{#1}%
+ \fi
+ }%
+ }%
+ \else
+ % It's not a partial table:
+ \def\tbl@firstp{1}%
+ \let\first@pq@index=\tbl@firstp
+ % We never get here on the first run of LaTeX, and
+ % \lastpage@withbonuspoints is defined on the second and later runs.
+ \def\tbl@lastp{\lastpage@withpoints}%
+ \let\last@pq@index=\tbl@lastp
+ \if@bonus
+ \def\tbl@lastp{\lastpage@withbonuspoints}%
+ \let\last@pq@index=\tbl@lastp
+ \fi
+ \if@combined
+ \ifnum \lastpage@withbonuspoints > \lastpage@withpoints\relax
+ \def\tbl@lastp{\lastpage@withbonuspoints}%
+ \let\last@pq@index=\tbl@lastp
+ \fi
+ \fi
+ \check@secondrun{#1}%
+ \fi
+}% find@prange
+
+\def\check@secondrun#1{%
+ % The function \ii@gtable already made sure that this isn't the
+ % first run of latex. To do a table indexed by pages, though, we
+ % have to also make sure it's not the second run of latex.
+ % We get here from \find@prange; the argument is either ``v'' or
+ % ``h''.
+ % Check that there's enough info from the .aux file to do a page
+ % indexed grade table. If so, call \tbl@v@or@h{#1}:
+ \@ifundefined{pointsonpage@\romannumeral
+ \csname lastpage@withpoints\endcsname}%
+ {\@ifundefined{bonuspointsonpage@\romannumeral
+ \csname lastpage@withbonuspoints\endcsname}%
+ {\ClassWarning{exam}{%
+ You must run LaTeX again to produce the table.\MessageBreak}%
+ \fbox{Run \LaTeX{} again to produce the table}%
+ }%
+ {\tbl@v@or@h{#1}%
+ }%
+ }%
+ {\tbl@v@or@h{#1}%
+ }%
+}% check@secondrun
+
+%--------------------------------------------------------------------
+% Indexed by pages:
+
+% For a table indexed by pages, we need to know how many pages there
+% are with points on them. The argument to \count@pgswpts should be
+% the name of a counter; we set that counter equal to the number of
+% pages with the appropriate kind of points.
+
+\def\count@pgswpts#1{%
+ % Set the counter #1 equal to the number of pages in the range with
+ % the appropriate type of points.
+ % We're called by \@computenumcols@h and \@computenumrows@v.
+ \setcounter{#1}{0}%
+ \setcounter{@iterator}{\tbl@firstp}%
+ \addtocounter{@iterator}{-1}%
+ \if@bonus
+ \docount@pgswbpts{#1}%
+ \else
+ \if@combined
+ \docount@pgswcpts{#1}%
+ \else
+ \docount@pgswpts{#1}%
+ \fi
+ \fi
+}% count@pgswpts
+
+\def\docount@pgswcpts#1{%
+ % Called by \count@pgswpts
+ % Count the number of pages in range with any kind of point (bonus
+ % or non-bonus):
+ \addtocounter{@iterator}{1}%
+ \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}%
+ \ifhlfcntr@pos{tmp@hlfcntr}%
+ \addtocounter{#1}{1}%
+ \else
+ \check@bnsptpage{#1}%
+ \fi
+ \ifnum \the@iterator < \tbl@lastp\relax
+ \def\nextdocount@pgswcpts{\docount@pgswcpts{#1}}%
+ \else
+ \let\nextdocount@pgswcpts=\relax
+ \fi
+ \nextdocount@pgswcpts
+}% docount@pgswcpts
+\def\check@bnsptpage#1{%
+ % We need to hide this inside of a macro because if \ifhlfcntr@pos
+ % isn't expanded (because this stuff is being skipped in the outer
+ % conditional), then TeX doesn't see the \ifnum hidden inside the
+ % \ifhlfcntr@pos, but it does see the \fi, and so it get confused.
+ \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}%
+ \ifhlfcntr@pos{tmp@hlfcntr}%
+ \addtocounter{#1}{1}%
+ \fi
+}% check@bnsptpage
+
+\def\docount@pgswpts#1{%
+ % Called by \count@pgswpts.
+ % Count the number of pages in range with regular points.
+ \addtocounter{@iterator}{1}%
+ \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}%
+ \ifhlfcntr@pos{tmp@hlfcntr}%
+ \addtocounter{#1}{1}%
+ \fi
+ \ifnum \the@iterator < \tbl@lastp\relax
+ \def\nextdocount@pgswpts{\docount@pgswpts{#1}}%
+ \else
+ \let\nextdocount@pgswpts=\relax
+ \fi
+ \nextdocount@pgswpts
+}% docount@pgswpts
+
+\def\docount@pgswbpts#1{%
+ % Called by \count@pgswpts
+ % Count the number of pages in range with bonus points.
+ \addtocounter{@iterator}{1}%
+ \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}%
+ \ifhlfcntr@pos{tmp@hlfcntr}%
+ \addtocounter{#1}{1}%
+ \fi
+ \ifnum \the@iterator < \tbl@lastp\relax
+ \def\nextdocount@pgswbpts{\docount@pgswbpts{#1}}%
+ \else
+ \let\nextdocount@pgswbpts=\relax
+ \fi
+ \nextdocount@pgswbpts
+}% docount@pgswbpts
+
+%--------------------------------------------------------------------
+%--------------------------------------------------------------------
+% Multirow horizontal tables, indexed by question numbers:
+
+\newcounter{pq@index}% In tables indexed by page numbers, it holds a
+% page number. In tables indexed by question numbers, it holds a
+% question number.
+
+\newcounter{pq@index@pts}% In horizontal tables, this holds either the
+% current page number or the current question number as we put the
+% point values for that page or question number into the table. In
+% vertical tables, this holds the index for the first column of the
+% current row.
+
+\newcounter{pq@index@bpts}% used to set bonus point values in
+% horizontal tables. Often used as scratch elsewhere.
+
+\def\hidden@ampersand{&}% Needed because an ampersand can't appear in
+% the replacement text of a conditional.
+
+\newif\iftbl@pgs
+% \tbl@pgstrue means a table indexed by page numbers
+% \tbl@pgsfalse means a table indexed by question numbers
+
+\newcounter{num@cols}
+\newcounter{num@rows}
+\newcounter{current@row}
+\newcounter{cols@done}% Holds the number of columns done in the
+% current row.
+
+%--------------------------------------------------------------------
+%--------------------------------------------------------------------
+% Stuff to unify tables indexed by questions and tables indexed by
+% pages:
+
+% \first@pq@index and \last@pq@index will hold either \tbl@firstq and
+% \tbl@lastq or \tbl@firstp and \tbl@lastp.
+
+\def\increment@index#1{%
+ % If we're doing a table indexed by question numbers, we increment
+ % the counter #1.
+ % If we're doing a table indexed by page numbers,
+ % we increase the counter #1 by at least 1 to either the number of the
+ % next page containing the appropriate kind of points, or to
+ % something greater than \tbl@lastp.
+ \iftbl@pgs
+ \find@nextpagewithpoints{#1}%
+ \else
+ \addtocounter{#1}{1}%
+ \fi
+}% increment@index
+
+\def\nextcolumn@index@v#1{%
+ % Used only for multicolumn tables.
+ % If we're doing a table indexed by question numbers, we increase
+ % the counter #1 by num@cols.
+ % If we're doing a table indexed by page numbers,
+ % we use \find@nextcolumnpage@v to increment the counter #1 to either
+ % the (num@rows)'th page number after #1 that contains the
+ % appropriate kind of points or to a value greater than \tbl@lastp.
+ \iftbl@pgs
+ \find@nextcolumnpage@v{#1}%
+ \else
+ \addtocounter{#1}{\value{num@rows}}%
+ \fi
+}% nextcolumn@index@v
+
+\def\pointsof@index#1{%
+ \iftbl@pgs
+ \pointsonpage{\arabic{#1}}%
+ \else
+ \pointsofquestion{\arabic{#1}}%
+ \fi
+}% pointsof@index
+
+\def\bonuspointsof@index#1{%
+ \iftbl@pgs
+ \bonuspointsonpage{\arabic{#1}}%
+ \else
+ \bonuspointsofquestion{\arabic{#1}}%
+ \fi
+}% bonuspointsof@index
+
+\def\refto@index#1{%
+ \iftbl@pgs
+ \if@combined
+ % Need to hide this inside of a macro:
+ \refto@comb@index{#1}%
+ \else
+ \if@bonus
+ \pageref{firstbonuspoints@onpage@\arabic{#1}}%
+ \else
+ \pageref{firstpoints@onpage@\arabic{#1}}%
+ \fi
+ \fi
+ \else
+ \ref{question@\arabic{#1}}%
+ \fi
+}% refto@index
+
+\def\refto@comb@index#1{%
+ % We're called only by \refto@index.
+ % We can't have the \ifhlfcntr@pos...\fi inside of another
+ % conditional, so we're hiding it in this macro.
+ \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\arabic{#1}}}%
+ \ifhlfcntr@pos{tmp@hlfcntr}%
+ \pageref{firstpoints@onpage@\arabic{#1}}%
+ \else
+ % In theory, there *must* be bonus points on this page, because
+ % there aren't any plain points, but there are allegedly *some*
+ % points. We're being brave and assuming that's correct, and not
+ % checking (which we'd have to hide inside a macro, because it
+ % would use \ifhlfcntr@pos):
+ \pageref{firstbonuspoints@onpage@\arabic{#1}}%
+ \fi
+}% refto@comb@index
+
+%--------------------------------------------------------------------
+%--------------------------------------------------------------------
+% Multirow tables:
+
+
+%--------------------------------------------------------------------
+% Check that the number of rows is OK, and compute the number of
+% columns:
+
+\def\check@num@rows@h{%
+ % We get here from \tbl@v@or@h.
+ % We make sure the number of rows is a positive integer. If it
+ % is, we go on to \@computenumcols@h
+ \ifnum \value{num@rows} < 1\relax
+ \ClassError{exam}{%
+ The number of rows in a table must be positive.\MessageBreak
+ }{%
+ The number of rows must be a positive integer.\MessageBreak
+ }%
+ \fbox{\textbf{Error:} Multirow table with no rows!}%
+ \else
+ \@computenumcols@h
+ \fi
+}% check@num@rows@h
+
+\def\@computenumcols@h{%
+ % We get here from \check@num@rows@h.
+ % Compute the number of columns.
+ % First: set num@cols to one more than either (the number of pages
+ % with the appropriate type of points) or (the number of questions),
+ % to have slots for the total along with the questions:
+ \iftbl@pgs
+ \count@pgswpts{num@cols}%
+ \addtocounter{num@cols}{1}%
+ \else
+ \setcounter{num@cols}{\tbl@lastq}%
+ \addtocounter{num@cols}{-\tbl@firstq}%
+ \addtocounter{num@cols}{2}%
+ \fi
+ % Save the number of slots needed in pq@index (used for scratch), to
+ % check for truncation:
+ \setcounter{pq@index}{\value{num@cols}}%
+ % Divide the number of slots needed by num@rows:
+ \divide \csname c@num@cols\endcsname by
+ \csname c@num@rows\endcsname
+ % Division truncates: See if there was truncation.
+ % Use @iterator as a scratch counter.
+ \setcounter{@iterator}{\value{num@cols}}%
+ \multiply \csname c@@iterator\endcsname by
+ \csname c@num@rows\endcsname
+ \ifnum \value{@iterator} < \value{pq@index}\relax
+ % There was truncation; add a column to num@cols:
+ \addtocounter{num@cols}{1}%
+ \fi
+ \@multirowtable
+}% @computenumcols@h
+
+%--------------------------------------------------------------------
+% Construct the actual table:
+
+\def\@multirowtable{%
+ % We get here from \@computenumcols@h.
+ % All multirow tables!
+ \renewcommand\arraystretch{\@gtblstretch}%
+ \set@hlfcntr{tbl@points}{0}%
+ \set@hlfcntr{tbl@bonuspoints}{0}%
+ \setcounter{pq@index}{\first@pq@index}%
+ \addtocounter{pq@index}{-1}%
+ \setcounter{pq@index@pts}{\value{pq@index}}%
+ \setcounter{pq@index@bpts}{\value{pq@index}}%
+ \setcounter{current@row}{0}%
+ \begin{tabular}{|l|*{\value{num@cols}}{c|}}
+ \hline
+ \if@combined
+ \do@comblines@h
+ \else
+ \do@lines@h
+ \fi
+}% @multirowtable
+
+
+\def\do@lines@h{%
+ % Called only by \@multirowtable.
+ % It's either bonus or regular, but not combined:
+ \addtocounter{current@row}{1}% Set to the number of the current row
+ \iftbl@pgs
+ \if@bonus
+ \@bhpgword
+ \else
+ \@hpgword
+ \fi
+ \else
+ \if@bonus
+ \@bhqword
+ \else
+ \@hqword
+ \fi
+ \fi
+ \setcounter{cols@done}{0}%
+ \do@pq@indexloop@h
+ % When we finish \do@pq@indexloop@h, either we've finished a
+ % complete row of page numbers (or questions), or we've done all
+ % the page numbers (or questions) through \last@pq@index, or both:
+ \ifnum \value{cols@done} < \value{num@cols}\relax
+ % We've inserted all the page or question numbers, and there's
+ % room remaining on the current line for \@htword (or \@bhtword):
+ \ifnum \value{current@row} = \value{num@rows}\relax
+ % This is the last row; put in the total:
+ \do@htword@h
+ \else
+ % This isn't the last row. We insert (\value{num@cols} -
+ % \value{cols@done}) ampersands.
+ \setcounter{@iterator}{\value{num@cols}}%
+ \addtocounter{@iterator}{-\value{cols@done}}%
+ \do@emptycols@h
+ \fi
+ \fi
+ \\
+ \hline
+ % Point values go here!
+ \setcounter{cols@done}{0}%
+ \if@bonus
+ \@bhpword
+ \do@bptloop@h
+ \else
+ \@hpword
+ \do@ptloop@h
+ \fi
+ % When we finish \do@ptloop@h or \do@bptloop@h, either
+ % we've finished a complete row of point values, or we've done all
+ % the question (or page) numbers through \last@pq@index, or both:
+ \ifnum \value{cols@done} < \value{num@cols}\relax
+ % We've inserted all the point values, and there's room
+ % remaining on the current line for Total Points:
+ \ifnum \value{current@row} = \value{num@rows}\relax
+ % This is the last row; put in the total:
+ \if@bonus
+ \do@totalbpts@h
+ \else
+ \do@totalpts@h
+ \fi
+ \else
+ % This isn't the last row. We insert (\value{num@cols} -
+ % \value{cols@done}) ampersands.
+ \setcounter{@iterator}{\value{num@cols}}%
+ \addtocounter{@iterator}{-\value{cols@done}}%
+ \do@emptycols@h
+ \fi
+ \fi
+ % We hold off on putting in the "\\ \hline" because we may want to
+ % immediately follow it with either an "\end{tabular}" or another
+ % "\hline".
+ % Scores?
+ \if@scores
+ \\
+ \hline
+ \if@bonus
+ \@bhsword \hidden@ampersand
+ \else
+ \@hsword \hidden@ampersand
+ \fi
+ \setcounter{cols@done}{0}%
+ \do@sloop@h
+ \fi
+ \ifnum \value{current@row} = \value{num@rows}\relax
+ % This is the last line! End the tabular:
+ \\
+ \hline
+ \end{tabular}%
+ \else
+ % Don't end the tabular:
+ \\
+ \hline\hline
+ \fi
+ % Check if we should repeat:
+ \ifnum \value{current@row} < \value{num@rows}\relax
+ \let\nextdo@lines@h=\do@lines@h
+ \else
+ \let\nextdo@lines@h=\relax
+ \fi
+ \nextdo@lines@h
+}% do@lines@h
+
+\def\do@comblines@h{%
+ % Called only by \@multirowtable.
+ % Combined tables.
+ \addtocounter{current@row}{1}% Set to the number of the current row
+ \iftbl@pgs
+ \@chpgword
+ \else
+ \@chqword
+ \fi
+ \setcounter{cols@done}{0}%
+ \do@pq@indexloop@h
+ % When we finish \do@pq@indexloop@h, either we've finished a
+ % complete row of page (or question) numbers, or we've done all
+ % the page (or question) numbers through \last@pq@index, or both:
+ \ifnum \value{cols@done} < \value{num@cols}\relax
+ % We've inserted all the question (or page) numbers, and there's
+ % room remaining on the current line for \@chtword:
+ \ifnum \value{current@row} = \value{num@rows}\relax
+ % This is the last row; put in the total:
+ \do@htword@h
+ \else
+ % This isn't the last row. We insert (\value{num@cols} -
+ % \value{cols@done}) ampersands.
+ \setcounter{@iterator}{\value{num@cols}}%
+ \addtocounter{@iterator}{-\value{cols@done}}%
+ \do@emptycols@h
+ \fi
+ \fi
+ \\
+ \hline
+ % Point values go here!
+ \@chpword
+ \setcounter{cols@done}{0}%
+ \do@ptloop@h
+ % When we finish \do@ptloop@h, either we've finished a complete
+ % row of point values, or we've done all the question (or page)
+ % numbers through \last@pq@index, or both:
+ \ifnum \value{cols@done} < \value{num@cols}\relax
+ % We've inserted all the point values, and there's room
+ % remaining on the current line for Total Points:
+ \ifnum \value{current@row} = \value{num@rows}\relax
+ % This is the last row; put in the total:
+ \do@totalpts@h
+ \else
+ % This isn't the last row. We insert (\value{num@cols} -
+ % \value{cols@done}) ampersands.
+ \setcounter{@iterator}{\value{num@cols}}%
+ \addtocounter{@iterator}{-\value{cols@done}}%
+ \do@emptycols@h
+ \fi
+ \fi
+ \\
+ \hline
+ % Bonus point values go here!
+ \@chbpword
+ \setcounter{cols@done}{0}%
+ \do@bptloop@h
+ % When we finish \do@bptloop@h, either
+ % we've finished a complete row of point values, or we've done all
+ % the question (or page) numbers through \last@pq@index, or both:
+ \ifnum \value{cols@done} < \value{num@cols}\relax
+ % We've inserted all the point values, and there's room
+ % remaining on the current line for Total Points:
+ \ifnum \value{current@row} = \value{num@rows}\relax
+ % This is the last row; put in the total:
+ \do@totalbpts@h
+ \else
+ % This isn't the last row. We insert (\value{num@cols} -
+ % \value{cols@done}) ampersands.
+ \setcounter{@iterator}{\value{num@cols}}%
+ \addtocounter{@iterator}{-\value{cols@done}}%
+ \do@emptycols@h
+ \fi
+ \fi
+ % We hold off on putting in the "\\ \hline" because we may want to
+ % immediately follow it with either an "\end{tabular}" or another
+ % "\hline".
+ % Scores?
+ \if@scores
+ \\
+ \hline
+ \@chsword \hidden@ampersand
+ \setcounter{cols@done}{0}%
+ \do@sloop@h
+ \fi
+ \ifnum \value{current@row} = \value{num@rows}\relax
+ % This is the last line! End the tabular:
+ \\
+ \hline
+ \end{tabular}%
+ \else
+ % Don't end the tabular:
+ \\
+ \hline\hline
+ \fi
+ % Check if we should repeat:
+ \ifnum \value{current@row} < \value{num@rows}\relax
+ \let\nextdo@comblines@h=\do@comblines@h
+ \else
+ \let\nextdo@comblines@h=\relax
+ \fi
+ \nextdo@comblines@h
+}% do@comblines@h
+
+\def\do@pq@indexloop@h{%
+ % Called by both \do@lines@h and \do@comblines@h.
+ % We insert at most one row of pq@index:
+ \increment@index{pq@index}%
+ \ifnum \value{pq@index} > \last@pq@index\relax
+ % Do nothing!
+ \else
+ \hidden@ampersand
+ \refto@index{pq@index}%
+ \addtocounter{cols@done}{1}%
+ \fi
+ \ifnum \value{pq@index} < \last@pq@index\relax
+ \ifnum \value{cols@done} < \value{num@cols}\relax
+ \let\nextdo@pq@indexloop@h=\do@pq@indexloop@h
+ \else
+ \let\nextdo@pq@indexloop@h=\relax
+ \fi
+ \else
+ \let\nextdo@pq@indexloop@h=\relax
+ \fi
+ \nextdo@pq@indexloop@h
+}% do@pq@indexloop@h
+
+\def\do@ptloop@h{%
+ % Called by both \do@lines@h and \do@comblines@h.
+ % We insert at most one row of non-bonus point values:
+ \increment@index{pq@index@pts}%
+ \ifnum \value{pq@index@pts} > \last@pq@index\relax
+ % Do nothing!
+ \else
+ \hidden@ampersand
+ \addtocounter{cols@done}{1}%
+ \pointsof@index{pq@index@pts}%
+ \addto@hlfcntr{tbl@points}{\pointsof@index{pq@index@pts}}%
+ \fi
+ \ifnum \value{pq@index@pts} < \last@pq@index\relax
+ \ifnum \value{cols@done} < \value{num@cols}\relax
+ \let\nextdo@ptloop@h=\do@ptloop@h
+ \else
+ \let\nextdo@ptloop@h=\relax
+ \fi
+ \else
+ \let\nextdo@ptloop@h=\relax
+ \fi
+ \nextdo@ptloop@h
+}% do@ptloop@h
+
+\def\do@bptloop@h{%
+ % Called by both \do@lines@h and \do@comblines@h.
+ % We insert at most one row of bonus point values:
+ \increment@index{pq@index@bpts}%
+ \ifnum \value{pq@index@bpts} > \last@pq@index\relax
+ % Do nothing!
+ \else
+ \hidden@ampersand
+ \addtocounter{cols@done}{1}%
+ \bonuspointsof@index{pq@index@bpts}%
+ \addto@hlfcntr{tbl@bonuspoints}{\bonuspointsof@index{pq@index@bpts}}%
+ \fi
+ \ifnum \value{pq@index@bpts} < \last@pq@index\relax
+ \ifnum \value{cols@done} < \value{num@cols}\relax
+ \let\nextdo@bptloop@h=\do@bptloop@h
+ \else
+ \let\nextdo@bptloop@h=\relax
+ \fi
+ \else
+ \let\nextdo@bptloop@h=\relax
+ \fi
+ \nextdo@bptloop@h
+}% do@bptloop@h
+
+\def\do@htword@h{%
+ % Called by both \do@lines@h and \do@comblines@h.
+ % We insert (\value{num@cols} - \value{cols@done}) ampersands,
+ % and then either \@htword or \@bhtword or \@chtword:
+ \setcounter{@iterator}{\value{num@cols}}%
+ \addtocounter{@iterator}{-\value{cols@done}}%
+ \do@emptycols@h
+ \if@combined
+ \@chtword
+ \else
+ \if@bonus
+ \@bhtword
+ \else
+ \@htword
+ \fi
+ \fi
+}% do@htword@h
+
+\def\do@totalpts@h{%
+ % Called by both \do@lines@h and \do@comblines@h.
+ % We insert (\value{num@cols} - \value{cols@done}) ampersands
+ % and then the total points:
+ \setcounter{@iterator}{\value{num@cols}}%
+ \addtocounter{@iterator}{-\value{cols@done}}%
+ \do@emptycols@h
+ \prt@tablepoints
+}% do@totalpts@h
+
+\def\do@totalbpts@h{%
+ % Called by both \do@lines@h and \do@comblines@h.
+ % We insert (\value{num@cols} - \value{cols@done}) ampersands,
+ % and then the total bonus points:
+ \setcounter{@iterator}{\value{num@cols}}%
+ \addtocounter{@iterator}{-\value{cols@done}}%
+ \do@emptycols@h
+ \prt@tablebonuspoints
+}% do@totalbpts@h
+
+\def\do@emptycols@h{%
+ % Called by \do@lines@h, \do@comblines@h, \do@htword@h,
+ % \do@totalpts@h, and \do@totalbpts@h.
+ % We insert \value{@iterator} ampersands:
+ \ifnum \value{@iterator} > 0\relax
+ \hidden@ampersand
+ \addtocounter{@iterator}{-1}%
+ \let\nextdo@emptycols@h=\do@emptycols@h
+ \else
+ \let\nextdo@emptycols@h=\relax
+ \fi
+ \nextdo@emptycols@h
+}% do@emptycols@h
+
+\def\do@sloop@h{%
+ % Called by both \do@lines@h and \do@comblines@h.
+ % We assume that cols@done has been set to zero.
+ % We insert num@cols \hbox to \@cellwidth,
+ % separated by ampersands.
+ \addtocounter{cols@done}{1}%
+ \hbox to \@cellwidth{\hfill}%
+ \ifnum \value{cols@done} < \value{num@cols}\relax
+ \hidden@ampersand
+ \let\nextdo@sloop@h=\do@sloop@h
+ \else
+ \let\nextdo@sloop@h=\relax
+ \fi
+ \nextdo@sloop@h
+}% do@sloop@h
+
+
+%--------------------------------------------------------------------
+%--------------------------------------------------------------------
+% Multicolumn tables
+
+
+%--------------------------------------------------------------------
+% Here's an example of a multicolumn grade table indexed by questions.
+
+% Every line of \cline's is followed by a
+% \noalign{\vskip\arrayrulewidth} to cancel the
+% \noalign{\vskip-\arrayrulewidth} that ends the definition of
+% \cline.
+
+% \begin{tabular}{*2{|c|c|c|c}}
+% \cline{1-3} \cline{5-7}
+% \noalign{\vskip\arrayrulewidth}
+% Question%
+% & Points%
+% & Score%
+% & \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}%
+% & Question%
+% & Points%
+% & Score%
+% \\
+% \cline{1-3} \cline{5-7}
+% \noalign{\vskip\arrayrulewidth}
+% 1%
+% & 5%
+% & \hbox to \@cellwidth{\hfill}%
+% & \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}%
+% & 4%
+% & 20%
+% & \hbox to \@cellwidth{\hfill}%
+% \\
+% \cline{1-3} \cline{5-7}
+% \noalign{\vskip\arrayrulewidth}
+% 2%
+% & 10%
+% & \hbox to \@cellwidth{\hfill}%
+% & \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}%
+% & 5%
+% & 25%
+% & \hbox to \@cellwidth{\hfill}%
+% \\
+% \cline{1-3} \cline{5-7}
+% \noalign{\vskip\arrayrulewidth}
+% 3%
+% & 15%
+% & \hbox to \@cellwidth{\hfill}%
+% & \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}%
+% & Total:%
+% & 75%
+% & \hbox to \@cellwidth{\hfill}%
+% \\
+% \cline{1-3} \cline{5-7}
+% \noalign{\vskip\arrayrulewidth}
+% \end{tabular}
+%--------------------------------------------------------------------
+%--------------------------------------------------------------------
+% Check that the number of cols is OK, and compute the number of rows:
+
+\def\check@num@cols@v{%
+ % We get here from \tbl@v@or@h.
+ % We make sure the number of cols is between 1 and 10 (since we
+ % can't handle more than 10 cols in a multicolumn table).
+ % If it is, we go on to \@computenumrows@v
+ \ifnum \value{num@cols} < 1\relax
+ \ClassError{exam}{%
+ The number of columns in a table must be positive.\MessageBreak
+ }{%
+ The number of columns must be a positive integer.\MessageBreak
+ }%
+ \fbox{\textbf{Error:} Multicolumn table with no columns!}%
+ \else
+ \ifnum \value{num@cols} > 10\relax
+ \ClassError{exam}{%
+ Multicolumn tables can have at most 10 columns.\MessageBreak
+ }{%
+ Multicolumn tables can have at most 10 columns.\MessageBreak
+ }%
+ \fbox{\textbf{Error:} Multicolumn table with more than 10 columns!}%
+ \else
+ \@computenumrows@v
+ \fi
+ \fi
+}% check@num@cols@v
+
+\def\@computenumrows@v{%
+ % We get here from \check@num@cols@v.
+ % Compute the number of rows.
+ % First: set num@rows to one more than the number of either
+ % (questions) or (pages with the appropriate type of points), to
+ % have slots for the total along with the questions or page numbers:
+ \iftbl@pgs
+ \count@pgswpts{num@rows}%
+ \addtocounter{num@rows}{1}%
+ \else
+ \setcounter{num@rows}{\last@pq@index}%
+ \addtocounter{num@rows}{-\first@pq@index}%
+ \addtocounter{num@rows}{2}%
+ \fi
+ % Save the number of slots needed, using pq@index@bpts as a scratch
+ % counter, to check for truncation on division:
+ \setcounter{pq@index@bpts}{\value{num@rows}}%
+ % Divide the number of slots needed by num@cols:
+ \divide \csname c@num@rows\endcsname by
+ \csname c@num@cols\endcsname
+ % Division truncates: See if there was truncation.
+ % Use the counter @iterator as a scratch counter:
+ \setcounter{@iterator}{\value{num@rows}}%
+ \multiply \csname c@@iterator\endcsname by
+ \csname c@num@cols\endcsname
+ \ifnum \value{@iterator} < \value{pq@index@bpts}\relax
+ % There was truncation; add one to num@rows:
+ \addtocounter{num@rows}{1}%
+ \fi
+ \@multicolumntable
+}% @computenumrows@v
+
+%--------------------------------------------------------------------
+% Construct the actual table:
+
+\def\@multicolumntable{%
+ % We get here from \@computenumrows@v.
+ % Set \cline@stuff@v equal to the line of \cline's:
+ \create@cline@stuff@v
+ \renewcommand\arraystretch{\@gtblstretch}%
+ \set@hlfcntr{tbl@points}{0}%
+ \set@hlfcntr{tbl@bonuspoints}{0}%
+ \if@combined
+ \if@scores
+ % combinedgradetable, possibly partial.
+ % Note: We'll never use the final "c" in the format of the
+ % tabular, but there's no harm in that.
+ \begin{tabular}{*{\value{num@cols}}{|c|c|c|c|c}}
+ % We need to make sure that the \cline@stuff@v commands come
+ % *immediately* following the \\ or \begin{tabular} (with no
+ % conditionals evaluated, even if those conditionals expand to
+ % the empty string)!
+ % Put in the row of column headings, with \cline@stuff@v above and
+ % below:
+ \cline@stuff@v
+ \setcounter{@iterator}{0}%
+ \docolumn@heads@comb@v
+ \\
+ \cline@stuff@v
+ \else
+ % combinedpointtable, possibly partial.
+ % Note: We'll never use the final "c" in the format of the
+ % tabular, but there's no harm in that.
+ \begin{tabular}{*{\value{num@cols}}{|c|c|c|c}}
+ % We need to make sure that the \cline@stuff@v commands come
+ % *immediately* following the \\ or \begin{tabular} (with no
+ % conditionals evaluated, even if those conditionals expand to
+ % the empty string)!
+ % Put in the row of column headings, with \cline@stuff@v above and
+ % below:
+ \cline@stuff@v
+ \setcounter{@iterator}{0}%
+ \docolumn@heads@comb@noscores@v
+ \\
+ \cline@stuff@v
+ \fi
+ % pq@index@pts will hold the question number (or page number) in
+ % the first column of the row.
+ \setcounter{pq@index@pts}{\first@pq@index}%
+ \iftbl@pgs
+ % If we're indexed by pages, we need to make sure there are
+ % points of the appropriate type on the first page listed:
+ \addtocounter{pq@index@pts}{-1}%
+ \find@nextpagewithpoints{pq@index@pts}%
+ \fi
+ \setcounter{current@row}{0}%
+ \do@lines@v
+ \else
+ % It's not combined:
+ \if@scores
+ % Note: We'll never use the final "c" in the format of the
+ % tabular, but there's no harm in that.
+ \begin{tabular}{*{\value{num@cols}}{|c|c|c|c}}
+ % We need to make sure that the \cline@stuff@v commands come
+ % *immediately* following the \\ or \begin{tabular} (with no
+ % conditionals evaluated, even if those conditionals expand to
+ % the empty string)!
+ % Put in the row of column headings, with \cline@stuff@v above and
+ % below:
+ \cline@stuff@v
+ \setcounter{@iterator}{0}%
+ \docolumn@heads@v
+ \\
+ \cline@stuff@v
+ \else
+ % Note: We'll never use the final "c" in the format of the
+ % tabular, but there's no harm in that.
+ \begin{tabular}{*{\value{num@cols}}{|c|c|c}}
+ % We need to make sure that the \cline@stuff@v commands come
+ % *immediately* following the \\ or \begin{tabular} (with no
+ % conditionals evaluated, even if those conditionals expand to
+ % the empty string)!
+ % Put in the row of column headings, with \cline@stuff@v above and
+ % below:
+ \cline@stuff@v
+ \setcounter{@iterator}{0}%
+ \docolumn@heads@noscores@v
+ \\
+ \cline@stuff@v
+ \fi
+ % pq@index@pts will hold the question number (or page number) in
+ % the first column of the row.
+ \setcounter{pq@index@pts}{\first@pq@index}%
+ \iftbl@pgs
+ % If we're indexed by pages, we need to make sure there are
+ % points of the appropriate type on the first page listed:
+ \addtocounter{pq@index@pts}{-1}%
+ \find@nextpagewithpoints{pq@index@pts}%
+ \fi
+ \setcounter{current@row}{0}%
+ \do@lines@v
+ \fi
+}% @multicolumntable
+
+%--------------------------------------------------------------------
+% \create@cline@stuff@v
+
+% The function \create@cline@stuff@v defines \cline@stuff@v to be whatever's
+% appropriate given the values of num@cols, \if@bonus, \if@combined, and
+% \if@scores.
+
+% We wimped out of generating \cline@stuff@v on the fly because we didn't
+% see how to get the correct expansions/nonexpansions without using a
+% primitive of e-TeX.
+
+% \clines@ii@whatever is for tables in which a logical column consists
+% of two columns; it's used for pointtable and bonuspointtable.
+
+\def\clines@ii@i{\cline{1-2}}
+\def\clines@ii@ii{\cline{1-2} \cline{4-5}}
+\def\clines@ii@iii{\cline{1-2} \cline{4-5} \cline{7-8}}
+\def\clines@ii@iv{\cline{1-2} \cline{4-5} \cline{7-8} \cline{10-11}}
+\def\clines@ii@v{\cline{1-2} \cline{4-5} \cline{7-8} \cline{10-11}
+ \cline{13-14}}
+\def\clines@ii@vi{\cline{1-2} \cline{4-5} \cline{7-8} \cline{10-11}
+ \cline{13-14} \cline{16-17}}
+\def\clines@ii@vii{\cline{1-2} \cline{4-5} \cline{7-8} \cline{10-11}
+ \cline{13-14} \cline{16-17} \cline{19-20}}
+\def\clines@ii@viii{\cline{1-2} \cline{4-5} \cline{7-8} \cline{10-11}
+ \cline{13-14} \cline{16-17} \cline{19-20} \cline{22-23}}
+\def\clines@ii@vix{\cline{1-2} \cline{4-5} \cline{7-8} \cline{10-11}
+ \cline{13-14} \cline{16-17} \cline{19-20} \cline{22-23}
+ \cline{25-26}}
+\def\clines@ii@x{\cline{1-2} \cline{4-5} \cline{7-8} \cline{10-11}
+ \cline{13-14} \cline{16-17} \cline{19-20} \cline{22-23}
+ \cline{25-26} \cline{28-29}}
+
+% \clines@iii@whatever is for tables in which a logical column consists
+% of three columns; it's are used for gradetable, bonusgradetable, and
+% combinedpointtable:
+
+\def\clines@iii@i{\cline{1-3}}
+\def\clines@iii@ii{\cline{1-3} \cline{5-7}}
+\def\clines@iii@iii{\cline{1-3} \cline{5-7} \cline{9-11}}
+\def\clines@iii@iv{\cline{1-3} \cline{5-7} \cline{9-11} \cline{13-15}}
+\def\clines@iii@v{\cline{1-3} \cline{5-7} \cline{9-11} \cline{13-15}
+ \cline{17-19}}
+\def\clines@iii@vi{\cline{1-3} \cline{5-7} \cline{9-11} \cline{13-15}
+ \cline{17-19} \cline{21-23}}
+\def\clines@iii@vii{\cline{1-3} \cline{5-7} \cline{9-11} \cline{13-15}
+ \cline{17-19} \cline{21-23} \cline{25-27}}
+\def\clines@iii@viii{\cline{1-3} \cline{5-7} \cline{9-11} \cline{13-15}
+ \cline{17-19} \cline{21-23} \cline{25-27} \cline{29-31}}
+\def\clines@iii@ix{\cline{1-3} \cline{5-7} \cline{9-11} \cline{13-15}
+ \cline{17-19} \cline{21-23} \cline{25-27} \cline{29-31}
+ \cline{33-35}}
+\def\clines@iii@x{\cline{1-3} \cline{5-7} \cline{9-11} \cline{13-15}
+ \cline{17-19} \cline{21-23} \cline{25-27} \cline{29-31}
+ \cline{33-35} \cline{37-39}}
+
+
+% \clines@iv@whatever is for tables in which a logical column
+% consists of four columns; it's used for combinedgradetable.
+
+\def\clines@iv@i{\cline{1-4}}
+\def\clines@iv@ii{\cline{1-4} \cline{6-9}}
+\def\clines@iv@iii{\cline{1-4} \cline{6-9} \cline{11-14}}
+\def\clines@iv@iv{\cline{1-4} \cline{6-9} \cline{11-14} \cline{16-19}}
+\def\clines@iv@v{\cline{1-4} \cline{6-9} \cline{11-14} \cline{16-19}
+ \cline{21-24}}
+\def\clines@iv@vi{\cline{1-4} \cline{6-9} \cline{11-14} \cline{16-19}
+ \cline{21-24} \cline{26-29}}
+\def\clines@iv@vii{\cline{1-4} \cline{6-9} \cline{11-14} \cline{16-19}
+ \cline{21-24} \cline{26-29} \cline{31-34}}
+\def\clines@iv@viii{\cline{1-4} \cline{6-9} \cline{11-14} \cline{16-19}
+ \cline{21-24} \cline{26-29} \cline{31-34} \cline{36-39}}
+\def\clines@iv@ix{\cline{1-4} \cline{6-9} \cline{11-14} \cline{16-19}
+ \cline{21-24} \cline{26-29} \cline{31-34} \cline{36-39}
+ \cline{41-44}}
+\def\clines@iv@x{\cline{1-4} \cline{6-9} \cline{11-14} \cline{16-19}
+ \cline{21-24} \cline{26-29} \cline{31-34} \cline{36-39}
+ \cline{41-44} \cline{46-49}}
+
+% The definition of \cline ends with \noalign{\vskip-\arrayrulewidth},
+% and so we want to throw in a \noalign{\vskip\arrayrulewidth} to
+% cancel that.
+\def\cline@correction{\noalign{\vskip\arrayrulewidth}}
+
+\def\create@cline@stuff@v{%
+ % Called by \@multicolumntable.
+ \if@combined
+ \if@scores
+ \edef\cline@stuff@v{\expandafter\noexpand\csname
+ clines@iv@\romannumeral \c@num@cols\endcsname
+ \noexpand\cline@correction}%
+ \else
+ \edef\cline@stuff@v{\expandafter\noexpand\csname
+ clines@iii@\romannumeral \c@num@cols\endcsname
+ \noexpand\cline@correction}%
+ \fi
+ \else
+ \if@scores
+ \edef\cline@stuff@v{\expandafter\noexpand\csname
+ clines@iii@\romannumeral \c@num@cols\endcsname
+ \noexpand\cline@correction}%
+ \else
+ \edef\cline@stuff@v{\expandafter\noexpand\csname
+ clines@ii@\romannumeral \c@num@cols\endcsname
+ \noexpand\cline@correction}%
+ \fi
+ \fi
+}% create@cline@stuff@v
+
+%--------------------------------------------------------------------
+% The various \docolumn@heads@something@v
+
+\def\docolumn@heads@v{%
+ % Called by \@multicolumntable.
+ % multicolumngradetable or multicolumnbonusgradetable, possibly
+ % partial.
+ \iftbl@pgs
+ \if@bonus
+ \@bvpgword
+ \else
+ \@vpgword
+ \fi
+ \else
+ \if@bonus
+ \@bvqword
+ \else
+ \@vqword
+ \fi
+ \fi
+ & \if@bonus
+ \@bvpword
+ \else
+ \@vpword
+ \fi
+ & \if@bonus
+ \@bvsword
+ \else
+ \@vsword
+ \fi
+ \addtocounter{@iterator}{1}%
+ \ifnum \value{@iterator} < \value{num@cols}\relax
+ \hidden@ampersand
+ \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}%
+ \hidden@ampersand
+ \let\nextdocolumn@heads@v=\docolumn@heads@v
+ \else
+ \let\nextdocolumn@heads@v=\relax
+ \fi
+ \nextdocolumn@heads@v
+}% docolumn@heads@v
+
+\def\docolumn@heads@noscores@v{%
+ % Called by \@multicolumntable.
+ % multicolumnpointtable or multicolumnbonuspointtable, possibly
+ % partial.
+ \iftbl@pgs
+ \if@bonus
+ \@bvpgword
+ \else
+ \@vpgword
+ \fi
+ \else
+ \if@bonus
+ \@bvqword
+ \else
+ \@vqword
+ \fi
+ \fi
+ &
+ \if@bonus
+ \@bvpword
+ \else
+ \@vpword
+ \fi
+ \addtocounter{@iterator}{1}%
+ \ifnum \value{@iterator} < \value{num@cols}\relax
+ \hidden@ampersand
+ \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}%
+ \hidden@ampersand
+ \let\nextdocolumn@heads@noscores@v=\docolumn@heads@noscores@v
+ \else
+ \let\nextdocolumn@heads@noscores@v=\relax
+ \fi
+ \nextdocolumn@heads@noscores@v
+}% docolumn@heads@noscores@v
+
+\def\docolumn@heads@comb@v{%
+ % Called by \@multicolumntable.
+ % multicolumncombinedgradetable, possibly partial.
+ \iftbl@pgs
+ \@cvpgword
+ \else
+ \@cvqword
+ \fi
+ & \@cvpword
+ & \@cvbpword
+ & \@cvsword
+ \addtocounter{@iterator}{1}%
+ \ifnum \value{@iterator} < \value{num@cols}\relax
+ \hidden@ampersand
+ \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}%
+ \hidden@ampersand
+ \let\nextdocolumn@heads@comb@v=\docolumn@heads@comb@v
+ \else
+ \let\nextdocolumn@heads@comb@v=\relax
+ \fi
+ \nextdocolumn@heads@comb@v
+}% docolumn@heads@comb@v
+
+\def\docolumn@heads@comb@noscores@v{%
+ % Called by \@multicolumntable.
+ % multicolumncombinedpointtable, possibly partial.
+ \iftbl@pgs
+ \@cvpgword
+ \else
+ \@cvqword
+ \fi
+ & \@vpword
+ & \@bvpword
+ \addtocounter{@iterator}{1}%
+ \ifnum \value{@iterator} < \value{num@cols}\relax
+ \hidden@ampersand
+ \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}%
+ \hidden@ampersand
+ \let\nextdocolumn@heads@comb@noscores@v=\docolumn@heads@comb@noscores@v
+ \else
+ \let\nextdocolumn@heads@comb@noscores@v=\relax
+ \fi
+ \nextdocolumn@heads@comb@noscores@v
+}% docolumn@heads@comb@noscores@v
+
+%--------------------------------------------------------------------
+% \do@lines@v is used by *all* multicolumn tables.
+% It calls \do@oneline@v for all non-combined tables and
+% \do@oneline@comb@v for all combined tables.
+
+\def\do@lines@v{%
+ % We get here from \@multicolumntable.
+ % ALL MULTICOLUMN TABLES!!!!
+ % pq@index@pts holds the question number or page number in the first
+ % column of the current row.
+ \addtocounter{current@row}{1}%
+ \setcounter{pq@index}{\value{pq@index@pts}}%
+ \setcounter{cols@done}{0}% Number of columns done
+ % We're doing both grade tables and point tables!!
+ \if@combined
+ \do@oneline@comb@v
+ \else
+ \do@oneline@v
+ \fi
+ \increment@index{pq@index@pts}%
+ % We need the "\\ \cline@stuff@v" to *immediately* precede the
+ % \end{tabular} (i.e., with no \ifnum separating them), to avoid
+ % having crap after the \cline@stuff@v that
+ % causes there to be an extra row at the end of the table. We also
+ % need there to be nothing between \\ and \cline@stuff@v.
+ \ifnum \value{current@row} = \value{num@rows}\relax
+ \\
+ \cline@stuff@v
+ \end{tabular}%
+ \let\nextdo@lines@v=\relax
+ \else
+ \\
+ \cline@stuff@v
+ \let\nextdo@lines@v=\do@lines@v
+ \fi
+ \nextdo@lines@v
+}% do@lines@v
+
+\def\do@oneline@v{%
+ % Called by \do@lines@v.
+ % Used for all multicolumn non-combined tables.
+ % pq@index holds the question or page number we're about to do.
+ \ifnum \value{pq@index} > \last@pq@index\relax
+ % See if we're in the last column; use pq@index@bpts as a scratch
+ % counter:
+ \setcounter{pq@index@bpts}{\value{cols@done}}%
+ \addtocounter{pq@index@bpts}{1}%
+ \ifnum \value{pq@index@bpts} = \value{num@cols}\relax
+ % We're in the last column; are we in the last row?
+ \ifnum \value{current@row} = \value{num@rows}\relax
+ % We're in the last column, last row!
+ % Print the total:
+ \if@bonus
+ \@bvtword
+ \else
+ \@vtword
+ \fi
+ \hidden@ampersand
+ \if@scores
+ \if@bonus
+ \prt@tablebonuspoints
+ \else
+ \prt@tablepoints
+ \fi
+ \hidden@ampersand
+ \hbox to \@cellwidth{\hfill}%
+ \else
+ \hspace*{\fill}%
+ \if@bonus
+ \prt@tablebonuspoints
+ \else
+ \prt@tablepoints
+ \fi
+ \fi
+ \else
+ % Not last column last row; insert empty space:
+ \hbox to \@cellwidth{\hfill}%
+ \if@scores
+ \hidden@ampersand
+ \hbox to \@cellwidth{\hfill}%
+ \fi
+ \hidden@ampersand
+ \hbox to \@cellwidth{\hfill}%
+ \fi
+ \else
+ % Not last column; insert empty space:
+ \hbox to \@cellwidth{\hfill}%
+ \if@scores
+ \hidden@ampersand
+ \hbox to \@cellwidth{\hfill}%
+ \fi
+ \hidden@ampersand
+ \hbox to \@cellwidth{\hfill}%
+ \fi
+ \else
+ % We need to do question (or page) number pq@index:
+ \refto@index{pq@index}%
+ \hidden@ampersand
+ \if@scores
+ \if@bonus
+ \bonuspointsof@index{pq@index}%
+ \addto@hlfcntr{tbl@bonuspoints}{\bonuspointsof@index{pq@index}}%
+ \else
+ \pointsof@index{pq@index}%
+ \addto@hlfcntr{tbl@points}{\pointsof@index{pq@index}}%
+ \fi
+ \hidden@ampersand
+ \hbox to \@cellwidth{\hfill}%
+ \else
+ \if@bonus
+ \bonuspointsof@index{pq@index}%
+ \addto@hlfcntr{tbl@bonuspoints}{\bonuspointsof@index{pq@index}}%
+ \else
+ \pointsof@index{pq@index}%
+ \addto@hlfcntr{tbl@points}{\pointsof@index{pq@index}}%
+ \fi
+ \fi
+ \fi
+ \addtocounter{cols@done}{1}% Number of columns done
+ \ifnum \value{cols@done} < \value{num@cols}\relax
+ \hidden@ampersand
+ \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}%
+ \nextcolumn@index@v{pq@index}%
+ \hidden@ampersand
+ \let\nextdo@oneline@v=\do@oneline@v
+ \else
+ \let\nextdo@oneline@v=\relax
+ \fi
+ \nextdo@oneline@v
+}% do@oneline@v
+
+\def\do@oneline@comb@v{%
+ % Called by \do@lines@v.
+ % All combined multicolumn tables.
+ % pq@index holds the question (or page) we're about to do.
+ \ifnum \value{pq@index} > \last@pq@index\relax
+ % See if we're in the last column; use pq@index@bpts as a scratch
+ % counter:
+ \setcounter{pq@index@bpts}{\value{cols@done}}%
+ \addtocounter{pq@index@bpts}{1}%
+ \ifnum \value{pq@index@bpts} = \value{num@cols}\relax
+ % We're in the last column; are we in the last row?
+ \ifnum \value{current@row} = \value{num@rows}\relax
+ % We're in the last column, last row!
+ % Print the total:
+ \@cvtword
+ \hidden@ampersand
+ \prt@tablepoints
+ \hidden@ampersand
+ \if@scores
+ \prt@tablebonuspoints
+ \hidden@ampersand
+ \hbox to \@cellwidth{\hfill}%
+ \else
+ \prt@tablebonuspoints
+ \fi
+ \else
+ % Last column, but not last row; insert empty space:
+ \hbox to \@cellwidth{\hfill}%
+ \hidden@ampersand
+ \hbox to \@cellwidth{\hfill}%
+ \if@scores
+ \hidden@ampersand
+ \hbox to \@cellwidth{\hfill}%
+ \fi
+ \hidden@ampersand
+ \hbox to \@cellwidth{\hfill}%
+ \fi
+ \else
+ % Not last column; insert empty space:
+ \hbox to \@cellwidth{\hfill}%
+ \hidden@ampersand
+ \hbox to \@cellwidth{\hfill}%
+ \if@scores
+ \hidden@ampersand
+ \hbox to \@cellwidth{\hfill}%
+ \fi
+ \hidden@ampersand
+ \hbox to \@cellwidth{\hfill}%
+ \fi
+ \else
+ % We need to do question number pq@index:
+ \refto@index{pq@index}%
+ \hidden@ampersand
+ \pointsof@index{pq@index}%
+ \addto@hlfcntr{tbl@points}{\pointsof@index{pq@index}}%
+ \hidden@ampersand
+ \if@scores
+ \bonuspointsof@index{pq@index}%
+ \addto@hlfcntr{tbl@bonuspoints}{\bonuspointsof@index{pq@index}}%
+ \hidden@ampersand
+ \hbox to \@cellwidth{\hfill}%
+ \else
+ \bonuspointsof@index{pq@index}%
+ \addto@hlfcntr{tbl@bonuspoints}{\bonuspointsof@index{pq@index}}%
+ \fi
+ \fi
+ \addtocounter{cols@done}{1}% Number of columns done
+ \ifnum \value{cols@done} < \value{num@cols}\relax
+ \hidden@ampersand
+ \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}%
+ \nextcolumn@index@v{pq@index}%
+ \hidden@ampersand
+ \let\nextdo@oneline@comb@v=\do@oneline@comb@v
+ \else
+ \let\nextdo@oneline@comb@v=\relax
+ \fi
+ \nextdo@oneline@comb@v
+}% do@oneline@comb@v
+
+%--------------------------------------------------------------------
+% \find@nextpagewithpoints and \find@nextcolumnpage@v:
+
+\def\find@nextpagewithpoints#1{%
+ % Called by \dofind@nextcolumnpage@v, \increment@index, and
+ % \@multicolumntable.
+ % The argument #1 should be the name of a counter with a nonnegative
+ % value.
+ % We increase #1 by at least 1 to either the number of the
+ % next page containing the appropriate kind of points, or to something
+ % greater than \tbl@lastp.
+ \addtocounter{#1}{1}%
+ \if@combined
+ \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\value{#1}}}%
+ \addto@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\value{#1}}}%
+ % The sum is positive when at least one of them is positive.
+ \else
+ \if@bonus
+ \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\value{#1}}}%
+ \else
+ \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\value{#1}}}%
+ \fi
+ \fi
+ \ifhlfcntr@pos{tmp@hlfcntr}%
+ \let\nextfind@nextpagewithpoints=\relax
+ \else
+ \ifnum \value{#1} > \tbl@lastp\relax
+ \let\nextfind@nextpagewithpoints=\relax
+ \else
+ \def\nextfind@nextpagewithpoints{\find@nextpagewithpoints{#1}}%
+ \fi
+ \fi
+ \nextfind@nextpagewithpoints
+}% find@nextpagewithpoints
+
+\def\find@nextcolumnpage@v#1{%
+ % Called by \nextcolumn@index@v.
+ % This is used for all multicolumn tables that are indexed by
+ % pages.
+ % We use \find@nextpagewithpoints to increment #1 to either
+ % the (num@rows)'th page number after #1 that contains the
+ % appropriate kind of points or to a value greater than \tbl@lastp.
+ % We use pq@index@bpts as a scratch counter.
+ \setcounter{pq@index@bpts}{0}%
+ \dofind@nextcolumnpage@v{#1}%
+}% find@nextcolumnpage@v
+\def\dofind@nextcolumnpage@v#1{%
+ % Called only by \find@nextcolumnpage@v.
+ \addtocounter{pq@index@bpts}{1}%
+ \find@nextpagewithpoints{#1}%
+ \ifnum \value{pq@index@bpts} = \value{num@rows}\relax
+ \let\nextdofind@nextcolumnpage@v=\relax
+ \else
+ % The following test shouldn't be needed, in theory, because the
+ % computation of num@cols should prevent trouble, but we're being
+ % paranoid.
+ \ifnum \value{#1} > \tbl@lastp\relax
+ \let\nextdofind@nextcolumnpage@v=\relax
+ \else
+ % Note: this is a \def, and not a \let, because we need to put
+ % in the argument #1:
+ \def\nextdofind@nextcolumnpage@v{\dofind@nextcolumnpage@v{#1}}%
+ \fi
+ \fi
+ \nextdofind@nextcolumnpage@v
+}% dofind@nextcolumnpage@v
+
+%--------------------------------------------------------------------
+% \pointsinrange and \bonuspointsinrange, and then
+% \firstqinrange, \lastqinrange, and \numqinrange.
+
+
+% We say either \@bonusfalse or \@bonustrue, and then we check it only
+% in \do@countloop:
+\def\pointsinrange#1{%
+ \@bonusfalse
+ \def\tbl@range{#1}%
+ \@ifundefined{exam@numpoints}%
+ {\mbox{\normalfont\bfseries ??}}%
+ {\read@range}%
+}% pointsinrange
+
+\def\bonuspointsinrange#1{%
+ \@bonustrue
+ \def\tbl@range{#1}%
+ \@ifundefined{exam@numpoints}%
+ {\mbox{\normalfont\bfseries ??}}%
+ {\read@range}%
+}% bonuspointsinrange
+
+\def\bad@range{%
+ % Called by \read@range, \firstqinrange, \lastqinrange, and
+ % \numqinrange.
+ {\mbox{\normalfont\bfseries ??}}%
+ \ClassWarning{exam}{%
+ Grading range `\tbl@range' not defined.\MessageBreak
+ \space\space Run LaTeX again.\MessageBreak
+ }%
+}% bad@range
+
+\def\read@range{%
+ % Called by \pointsinrange and \bonuspointsinrange.
+ \@ifundefined{range@\tbl@range @firstq}%
+ {%
+ \bad@range
+ }%
+ {%
+ \@ifundefined{range@\tbl@range @lastq}%
+ {%
+ \bad@range
+ }%
+ {%
+ \edef\tbl@firstq{\csname range@\tbl@range @firstq\endcsname}%
+ \edef\tbl@lastq{\csname range@\tbl@range @lastq\endcsname}%
+ % Check that firstq precedes or equals lastq:
+ \ifnum \tbl@firstq > \tbl@lastq\relax
+ \fbox{\textbf{Error:} Grading Range `\tbl@range ':
+ Last question precedes first question.}%
+ \ClassError{exam}{%
+ In grading range `\tbl@range ',
+ the last question\MessageBreak
+ \space\space comes before the first question.\MessageBreak
+ }{%
+ \string\begingradingrange \space must precede
+ \string\endgradingrange \space by at
+ least one question.\MessageBreak
+ }%
+ \else
+ \count@pointsinrange
+ \fi
+ }%
+ }%
+}% read@range
+
+\def\count@pointsinrange{%
+ % Used for both \pointsinrange and \bonuspointsinrange:
+ \set@hlfcntr{tbl@points}{0}%
+ \setcounter{@iterator}{\tbl@firstq}%
+ \addtocounter{@iterator}{-1}\do@countloop
+ \prt@hlfcntr{tbl@points}%
+}% count@pointsinrange
+\def\do@countloop{%
+ % We check \if@bonus here when needed:
+ \addtocounter{@iterator}{1}%
+ \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
+
+%--------------------------------------------------------------------
+% \firstqinrange, \lastqinrange, and \numqinrange.
+
+\newcommand{\firstqinrange}[1]{%
+ \def\tbl@range{#1}%
+ \@ifundefined{range@\tbl@range @firstq}%
+ {\bad@range}%
+ {\csname range@#1@firstq\endcsname}%
+}% firstqinrange
+
+\newcommand{\lastqinrange}[1]{%
+ \def\tbl@range{#1}%
+ \@ifundefined{range@\tbl@range @lastq}%
+ {\bad@range}%
+ {\csname range@#1@lastq\endcsname}%
+}% lastqinrange
+
+\newcommand{\numqinrange}[1]{%
+ \def\tbl@range{#1}%
+ \@ifundefined{range@#1@firstq}%
+ {%
+ \bad@range
+ }%
+ {%
+ \@ifundefined{range@#1@lastq}%
+ {%
+ \bad@range
+ }%
+ {%
+ \setcounter{@iterator}{\csname range@#1@lastq\endcsname}%
+ \addtocounter{@iterator}{-\csname range@#1@firstq\endcsname}%
+ \stepcounter{@iterator}%
+ \arabic{@iterator}%
+ }%
+ }%
+}% numqinrange
+
+
+%--------------------------------------------------------------------
+%--------------------------------------------------------------------
+%
+% ***************************
+% ** SOLUTION ENVIRONMENTS **
+% ***************************
+
+
+
+% If the documentclass options include ``answers'', then the command
+% \printanswerstrue is given at the beginning of the run.
+
+% If the documentclass options include ``noanswers'', then the command
+% \printanswersfalse is given at the beginning of the run.
+
+\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}
+
+% \if@insolution will be true while we're inside of any of the
+% solution environments. This is used to supress \PgInfo@write and
+% \label commands generated if there's a parts (or subparts, or
+% subsubparts) environment inside of a solution. (It won't suppress
+% the labels for the question objects, since a question object is
+% never a label that's been used before.)
+\newif\if@insolution
+\@insolutionfalse
+
+\newcommand\SolutionEmphasis[1]{%
+ \def\Solution@Emphasis{#1}%
+}
+\SolutionEmphasis{}
+
+% If printanswers is true, we print the solution using a TheSolution
+% environment. If printanswers is false and cancelspace is false, we
+% insert blank vertical space equal to the optional argument (the
+% default value of which is 0pt).
+\newenvironment{solution}[1][0pt]%
+ {%
+ \@insolutiontrue % cancelled by the end of the environment
+ \@addpointsfalse % cancelled by the end of the environment
+ \ifprintanswers
+ \begingroup
+ \Solution@Emphasis
+ \begin{TheSolution}%
+ \else
+ \ifcancelspace
+ % Do nothing
+ \else
+ \par
+ \penalty 0
+ \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 and cancelspace is false,
+% we insert an empty box of width the current line width and of
+% height equal to the optional argument, which can be a length, or
+% \fill, or \stretch{number}. If the optional argument is omitted,
+% then the box is entirely omitted when printanswers is false.
+\newenvironment{solutionorbox}[1][-1pt]%
+ {%
+ \@insolutiontrue % cancelled by the end of the environment
+ \@addpointsfalse % cancelled by the end of the environment
+ \ifprintanswers
+ \begingroup
+ \Solution@Emphasis
+ \begin{TheSolution}%
+ \else
+ \ifcancelspace
+ % Do nothing
+ \else
+ \par
+ % Note: It's important that the following test be
+ % ``\ifdim 0pt > #1'' rather than ``\ifdim #1 < 0pt''
+ % That's because if the user says
+ % ``\begin{solutionorbox}{\stretch{1}}''
+ % (or \stretch{anythingelse}), then this will expand to
+ % ``\ifdim 0pt > \z@ plus 1fill\relax''.
+ % The \ifdim will be ``\ifdim 0pt > \z@'', and we'll have
+ % ``plus 1fill\relax'' left over. This is OK because if the
+ % \ifdim is false, that leftover stuff will be ignored,
+ % and it will only be true if the user omitted the optional
+ % argument, in which case there's no \stretch and thus no
+ % left over part.
+ % If we said ``\ifdim #1 < 0pt'', then we'd get an error
+ % when the user used \stretch, since the leftover stuff
+ % would appear when TeX was looking for <, =, or >.
+ \ifdim 0pt > #1
+ % do nothing
+ \else
+ \makeemptybox{#1}%
+ \fi
+ \fi
+ \setbox\z@\vbox\bgroup
+ \fi
+ }{%
+ \ifprintanswers
+ \end{TheSolution}%
+ \endgroup
+ \else
+ \egroup
+ \fi
+ }%
+
+% If printanswers is true, we print the solution using a TheSolution
+% environment. If printanswers is false and cancelspace is false,
+% we insert lined vertical space equal to the optional argument (the
+% default value of which is 0pt).
+\newenvironment{solutionorlines}[1][0pt]%
+ {%
+ \@insolutiontrue % cancelled by the end of the environment
+ \@addpointsfalse % cancelled by the end of the environment
+ \ifprintanswers
+ \begingroup
+ \Solution@Emphasis
+ \begin{TheSolution}%
+ \else
+ \ifcancelspace
+ % Do nothing
+ \else
+ \par
+ \penalty 0
+ \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 and cancelspace is false,
+% we insert dotted lined vertical space equal to the optional
+% argument (the default value of which is 0pt).
+\newenvironment{solutionordottedlines}[1][0pt]%
+ {%
+ \@insolutiontrue % cancelled by the end of the environment
+ \@addpointsfalse % cancelled by the end of the environment
+ \ifprintanswers
+ \begingroup
+ \Solution@Emphasis
+ \begin{TheSolution}%
+ \else
+ \ifcancelspace
+ % Do nothing
+ \else
+ \par
+ \penalty 0
+ \fillwithdottedlines{#1}%
+ \fi
+ \setbox\z@\vbox\bgroup
+ \fi
+ }{%
+ \ifprintanswers
+ \end{TheSolution}%
+ \endgroup
+ \else
+ \egroup
+ \fi
+ }%
+
+% If printanswers is true, we print the solution using a TheSolution
+% environment. If printanswers is false and cancelspace is false,
+% we insert a grid occupying vertically the optional argument (the
+% default value of which is 0pt).
+\newenvironment{solutionorgrid}[1][0pt]%
+ {%
+ \@insolutiontrue % cancelled by the end of the environment
+ \@addpointsfalse % cancelled by the end of the environment
+ \ifprintanswers
+ \begingroup
+ \Solution@Emphasis
+ \begin{TheSolution}%
+ \else
+ \ifcancelspace
+ % Do nothing
+ \else
+ \par
+ \penalty 0
+ \fillwithgrid{#1}%
+ \fi
+ \setbox\z@\vbox\bgroup
+ \fi
+ }{%
+ \ifprintanswers
+ \end{TheSolution}%
+ \endgroup
+ \else
+ \egroup
+ \fi
+ }%
+
+
+% The environment TheSolution is called from the solution,
+% solutionorbox, solutionorlines, solutionordottedlines, and
+% solutionorgrid environments when printanswers is true. It uses
+% Donald Arseneau's framed.sty macros (included at the end of this
+% file) to allow the solution to be broken across pages and have each
+% piece enclosed in an fbox (or a colorbox, if the user has given the
+% command \shadedsolutions), (or no box at all, if the user has given
+% the command \unframedsolutions).
+
+% Of course, the user can change TheSolution with a \renewenvironment
+% command.
+\newcommand{\solutiontitle}{\noindent\textbf{Solution:}\enspace}
+\newenvironment{TheSolution}%
+ {%
+ \vspace{\parskip}%
+ % If we don't set \leftskip and \rightskip to 0pt, then if we
+ % appear inside of an \uplevel command we'd have indentation
+ % 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
+ % We'll use the default \exam@FrameCommand
+ \else
+ \if@shadedsolutions
+ \def\exam@FrameCommand{\colorbox{SolutionColor}}%
+ \else
+ % It's \unframedsolutions:
+ \def\exam@FrameCommand{}%
+ \fi
+ \fi
+ \exam@MakeFramed{\advance\hsize-\exam@width}%
+ \solutiontitle
+ \ignorespaces
+ }%
+ {%
+ \unskip
+ \endexam@MakeFramed
+ }%
+
+\newif\if@framedsolutions
+\@framedsolutionstrue
+\newif\if@shadedsolutions
+\@shadedsolutionsfalse
+% If the user said \unframedsolutions, then both
+% \if@framedsolutions and \if@shadedsolutions are false.
+
+\def\framedsolutions{\@framedsolutionstrue\@shadedsolutionsfalse}
+\def\shadedsolutions{%
+ \@ifundefined{definecolor}
+ {%
+ \ClassError{exam}{%
+ You must load the color package with the command\MessageBreak
+ \space\space\protect\usepackage{color}\MessageBreak
+ in order to use the command \protect\shadedsolutions
+ \MessageBreak
+ }{%
+ This command makes use of the package color.sty,\MessageBreak
+ and so you have to load color.sty before your\MessageBreak
+ \protect\begin{document} command.\MessageBreak
+ }%
+ }%
+ {%
+ \definecolor{SolutionColor}{gray}{0.8}
+ \@shadedsolutionstrue
+ \@framedsolutionsfalse
+ }%
+}
+\def\unframedsolutions{\@framedsolutionsfalse\@shadedsolutionsfalse}
+
+
+% The solutionbox environment is different from the other solution
+% environments (solution, solutionorbox, solutionorlines,
+% solutionordottedlines, and solutionorgrid), in that
+%
+% (1) The box is always printed, whether answers are being printed
+% or not.
+%
+% (2) The argument giving the size of the box is a required
+% argument, not an optional argument, and so it should be enclosed
+% in braces, not in brackets. It can be either a length or
+% \stretch{number}.
+%
+% (3) We make no use of the TheSolution environment; the solutionbox
+% environment is completely freestanding.
+%
+% If answers are not being printed then only the box is printed, with
+% nothing in it. If answers are being printed, then the solution is
+% printed inside of the box.
+%
+% Note: It's the user's responsibility to be sure that the box is
+% large enough to hold the solution! If the solution takes up too
+% much vertical space, then it will spill out of the bottom of the
+% box, overwriting whatever follows the box.
+
+% 2016/02/08: The solutionbox frame can now be printed in color, as
+% long as you load color.sty in the preamble.
+%
+% Usage: Say
+%
+% \usepackage{color}
+%
+% in the preamble, and then give the command
+%
+% \colorsolutionboxes
+%
+% to have the frame around a solutionbox in color. The default color
+% was created by the command
+%
+% \definecolor{SolutionBoxColor}{gray}{0.8}
+%
+% and you can change the color by giving a new \definecolor command
+% (which must be done *after* the \colorsolutionboxes command).
+%
+% To cancel color solutionbox frames and return to black, give the
+% command
+%
+% \nocolorsolutionboxes
+
+\newif\if@colorsolutionboxes
+\@colorsolutionboxesfalse
+\def\colorsolutionboxes{%
+ \@ifundefined{definecolor}
+ {%
+ \ClassError{exam}{%
+ You must load the color package with the command\MessageBreak
+ \space\space\protect\usepackage{color}\MessageBreak
+ in order to use the command \protect\colorsolutionboxes
+ \MessageBreak
+ }{%
+ This command makes use of the package color.sty,\MessageBreak
+ and so you have to load color.sty before your\MessageBreak
+ \protect\begin{document} command.\MessageBreak
+ }%
+ }%
+ {%
+ \definecolor{SolutionBoxColor}{gray}{0.8}
+ \@colorsolutionboxestrue
+ }%
+}
+\def\nocolorsolutionboxes{\@colorsolutionboxesfalse}
+
+\newbox\exam@box
+\newenvironment{solutionbox}[1]{%
+ \@insolutiontrue % cancelled by the end of the environment
+ \@addpointsfalse % cancelled by the end of the environment
+ \def\solutionbox@size{#1}% saved for end of environment
+% Change, 2016/02/08: So that the solutionbox environment will work
+% correctly inside of a tabular environment, we use \hsize instead of
+% \textwidth:
+% \@tempdima=\textwidth
+ \@tempdima=\hsize
+ \advance\@tempdima -\@totalleftmargin
+ \advance\@tempdima -6\fboxsep
+ \advance\@tempdima -2\fboxrule
+ % Confine the \Solution@Emphasis, as well as anything the user puts
+ % into the solution (e.g., \color{red}, or whatever); don't say
+ % \endgroup until after using \box\exam@box:
+ \begingroup
+ \Solution@Emphasis
+ % We save the solution in a box of the proper width. We'll either
+ % print it (if we're printing solutions) or throw it away by just
+ % not using it before the environment ends:
+ \setbox\exam@box=\vtop\bgroup
+ \hsize=\@tempdima
+ \leftskip=0pt
+ \rightskip=0pt
+ \vskip 2\fboxsep
+% Change, 2016/05/09: We change \@totalleftmargin and \linewidth in
+% case there are enumerate, itemize, or description environments
+% inside the solution:
+ \@totalleftmargin=0pt
+ \linewidth=\hsize
+ \solutiontitle
+ \ignorespaces
+ }%
+ {%
+ \unskip
+ \egroup
+ % OK, the solution is now inside \box\exam@box.
+ % Set the height and depth to 0pt, so that if we use it we won't
+ % be advancing our position on the page:
+ \ht\exam@box=0pt
+ \dp\exam@box=0pt
+ \par
+ \vspace{\parskip}
+ \ifprintanswers
+ % We enclose the \vtop in an \hbox to avoid having the
+ % indentation of the enclosing list environment (implemented via
+ % \parshape) shift us to the right when we enter horizontal
+ % mode. If we don't use this \hbox, then we'd have to comment
+ % out the \hskip \@totalleftmargin:
+ % 2016/02/08: Changed \textwidth to \hsize:
+ \hbox to \hsize{%
+ \noindent
+ \hskip\@totalleftmargin
+ \hskip3\fboxsep\hskip\fboxrule
+ \box\exam@box\hfill
+ }%
+ \par\nointerlineskip
+ \fi
+ \endgroup % Finish confining the \Solution@Emphasis
+% Starting in version 2.502, 2016/03/23,the decision of whether to
+% color the box is made in the \makeemptybox command:
+ \makeemptybox{\solutionbox@size}
+ }% End of the second argument of \newenvironment{solutionbox}
+
+
+%--------------------------------------------------------------------
+%--------------------------------------------------------------------
+% Added in version 2.502: 2016/03/23, \colorfbox
+
+% The \colorfbox command is used in our modification of framed.sty
+% that allows us to print the frame around the solution in color when
+% the user has given the command \colorsolutionboxes. It takes two
+% arguments, the first being the color for the frame, and the second
+% being the stuff to be framed.
+
+% If we had assumed that xcolor.sty was used (instead of just
+% color.sty), then the line that saves the current color in
+% saved@color could have been just
+
+% \colorlet{saved@color}{.}
+
+% but we wanted to make this work even if color.sty is being used.
+
+% When you define a color mycolor using either color.sty or
+% xcolor.sty, a macro \csname\string\color@ mycolor\endcsname is
+% defined (i.e., the macro name is \\color@mycolor).
+\newcommand{\colorfbox}[2]{%
+ % Save the current color in saved@color:
+ \expandafter\let\csname\string\color@saved@color\endcsname\current@color
+ % Create the box in color #1, with the text in saved@color
+ % (the braces are to confine the color change commands):
+ {\color{#1}\fbox{\color{saved@color}#2}}%
+}% colorfbox
+
+%--------------------------------------------------------------------
+%--------------------------------------------------------------------
+
+% The following stuff is lifted from:
+%
+% framed.sty v 0.8a 21-Jul-2003
+% Copyright (C) 1992-2003 by Donald Arseneau
+% These macros may be freely transmitted, reproduced, or modified
+% provided that this notice is left intact.
+%
+% The modifications I made are marked with ``psh'' in a comment:
+%
+% Further modifications, 2017-09-21
+% I changed the names of many commands by prepending ``exam@'', so
+% that the user can use the framed.sty package with exam.cls and not
+% have conflicts. (I also renamed the framed, shaded, and leftbar
+% environments to examframed, examshaded, and examleftbar.) I didn't
+% mark these name changes.
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Create framed or shaded regions that can break across pages using
+% \begin{examframed} ... \end{examframed} -- ordinary frame box
+% (box at margin)
+% \begin{examshaded} ... \end{examshaded} -- shaded background
+% (into margin)
+% ... examleftbar ... -- line on left side
+% \begin{MakeFramed}{settings} ... \end{MakeFramed}
+% -- generic frame (for new environments)
+%
+% The "examframed" environment puts the text into "\fbox" with the
+% settings "\fboxrule=\exam@FrameRule" and "\fboxsep=\exam@FrameSep".
+% You can change these lengths (using "\setlength") and you
+% can even change the definition of "\exam@FrameCommand" to use
+% much fancier boxes.
+%
+% In fact, the "shaded" environment just redefines "\exam@FrameCommand"
+% to use "\colorbox{shadecolor}" (and you have to define the
+% color "shadecolor": \newcolor{shadecolor}...).
+%
+% A page break is allowed, and even encouraged, before the framed
+% environment. If you want to attach some text (a box title) to the
+% frame, then the text should be inserted by \exam@FrameCommand
+%
+% The contents of the framed regions are restricted:
+% Floats, footnotes, marginpars and head-line entries will be lost.
+% (Some of these may be handled in a later version.)
+% This package will not work with the page breaking of multicol.sty,
+% or other systems that perform column-balancing.
+%
+% The MakeFramed environment does the work. Its "settings" argument
+% should contain any adjustments to the text width (applied to \hsize,
+% and using the "\exam@width" of the frame itself) as well as a `restore'
+% command -- \@parboxrestore or \exam@FrameRestore or something similar.
+%
+% Expert commands:
+% \exam@MakeFramed, \endexam@MakeFramed: the "MakeFramed" environment
+% \exam@FrameCommand: command to draw the frame around its argument
+% \exam@FrameRestore: restore some text settings, but fewer than
+% \@parboxrestore
+% \exam@FrameRule: length register; \fboxrule for default "framed".
+% \exam@FrameSep: length register; \fboxsep for default "framed".
+% \exam@frameHeightAdjust: macro; height of frame above baseline at
+% top of page
+%
+% This is still a `pre-production' version because I can think of many
+% features/improvements that should be made. Nevertheless, starting
+% with version 0.5 it should be bug-free.
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%psh: Commented out \ProvidesPackage:
+%\ProvidesPackage{framed}[2003/07/21 v 0.8a:
+% framed or shaded text with page breaks]
+
+%psh: Created \saved@totalleftmargin and \@sollistdepth:
+\newdimen\saved@totalleftmargin
+\newcount\@sollistdepth
+
+\newenvironment{examframed}% using default \exam@FrameCommand
+ {\exam@MakeFramed {\advance\hsize-\exam@width \exam@FrameRestore}}%
+ {\endexam@MakeFramed}
+
+\newenvironment{examshaded}{%
+ \def\exam@FrameCommand{\colorbox{shadecolor}}%
+ \exam@MakeFramed {\exam@FrameRestore}}%
+ {\endexam@MakeFramed}
+
+\newenvironment{examleftbar}{%
+ \def\exam@FrameCommand{\vrule width 3pt \hspace{10pt}}%
+ \exam@MakeFramed {\advance\hsize-\exam@width \exam@FrameRestore}}%
+ {\endexam@MakeFramed}
+
+\chardef\exam@FrameRestore=\catcode`\| % for debug
+\catcode`\|=\catcode`\% % (debug: insert space after backslash)
+
+\def\exam@MakeFramed#1{\par
+ % measure added width and height; call result \exam@width and \exam@height
+ \setbox\z@\vbox{\vskip-1in \hbox{\hskip-1in
+ \exam@FrameCommand{\hbox{\vrule \@height .7in \@depth.3in \@width 1in}}}%
+ \vskip\z@skip}%
+ \def\exam@width{\wd\z@}\def\exam@height{\ht\z@}%
+ \edef\exam@fb@frw{\the\exam@width}\edef\exam@fb@frh{\the\exam@height}%
+ % insert pre-penalties and skips
+ \begingroup
+ \skip@\lastskip
+ \if@nobreak\else
+ \penalty9999 % updates \page parameters
+ \ifdim\pagefilstretch=\z@ \ifdim\pagefillstretch=\z@
+ \edef\@tempa{\the\skip@}%
+ \ifx\@tempa\exam@zero@glue \penalty-30
+ \else \vskip-\skip@ \penalty-30 \vskip\skip@
+ \fi\fi\fi
+ \penalty\z@
+ % Give a stretchy breakpoint that will always be taken in preference
+ % to the \penalty 9999 used to update page parameters. The cube root
+ % of 10000/100 indicates a multiplier of 0.21545, but the maximum
+ % calculated badness is really 8192, not 10000, so the multiplier
+ % is 0.2301.
+ \advance\skip@ \z@ plus-.5\baselineskip
+ \advance\skip@ \z@ plus-.231\exam@height
+ \advance\skip@ \z@ plus-.231\skip@
+ \advance\skip@ \z@ plus-.231\topsep
+ \vskip-\skip@ \penalty 1800 \vskip\skip@
+ \fi
+ \addvspace{\topsep}%
+ \endgroup
+ % clear out pending page break
+ \penalty\@M \vskip 2\baselineskip \vskip\exam@height
+ \penalty9999 \vskip -2\baselineskip \vskip-\exam@height
+ \penalty9999 % updates \pagetotal
+|\message{After clearout, \pagetotal=\the\pagetotal, \pagegoal=\the\pagegoal. }%
+ \exam@fb@adjheight
+%psh: Added commands:
+ \advance\hsize-\@totalleftmargin
+ \saved@totalleftmargin=\@totalleftmargin
+ \@totalleftmargin=0pt
+ \parshape 0
+ \let\@listdepth=\@sollistdepth
+ \@sollistdepth=0
+ \leftmargin=0pt
+%psh: end of added commands
+ \setbox\@tempboxa\vbox\bgroup
+ #1% Modifications to \hsize (can use \exam@width and \exam@height)
+ \textwidth\hsize \columnwidth\hsize
+%psh: added one line:
+ \linewidth=\hsize
+}
+
+\def\endexam@MakeFramed{\par
+ \kern\z@ \penalty-100 % put depth into height
+ \egroup
+ \begingroup \exam@put@frame \endgroup
+%psh: Added one line:
+ \@totalleftmargin=\saved@totalleftmargin
+}
+
+% \exam@put@frame takes the contents of \@tempboxa and puts all, or a
+% piece, of it on the page with a frame (\exam@FrameCommand). It
+% recurses until all of \@tempboxa has been used up. (\@tempboxa must
+% have zero depth.)
+
+\def\exam@put@frame{\relax
+ \ifdim\pagegoal=\maxdimen \pagegoal\vsize \fi
+| \message{=============== Entering putframe ====================^^J
+| \pagegoal=\the\pagegoal, \pagetotal=\the\pagetotal. }%
+ \ifinner \else
+ \dimen@\pagegoal \advance\dimen@-\pagetotal % natural space left on page
+ \ifdim\dimen@<2\baselineskip
+| \message{Page has only \the\dimen@\space room left; eject. }%
+ \eject \exam@fb@adjheight \exam@put@frame
+ \else % there's appreciable room left on the page
+| \message{\string\pagetotal=\the\pagetotal,
+| \string\pagegoal=\the\pagegoal,
+| \string\pagestretch=\the\pagestretch,
+| \string\pageshrink=\the\pageshrink,
+| \string\exam@fb@frh=\exam@fb@frh. \space}
+| \message{Box of size \the\ht\@tempboxa\space + \exam@fb@frh}%
+ \begingroup % temporarily set \dimen@ to be...
+ \advance\dimen@.8\pageshrink % maximum space available on page
+ \advance\dimen@-\exam@fb@frh\relax % space available for frame's contents
+ \expandafter\endgroup
+ % restore \dimen@ to real room left on page
+ \ifdim\dimen@>\ht\@tempboxa % whole box does fit
+| \message{fits in \the\dimen@. }%
+ \else % box must be split
+| \message{must be split to fit in \the\dimen@. }%
+ \setbox\@tempboxa\vbox{% simulate frame and flexiblity of the page:
+ \vskip \exam@fb@frh \@plus\pagestretch \@minus.8\pageshrink
+ \kern137sp\kern-137sp\penalty-30
+ \unvbox\@tempboxa}%
+ \edef\exam@fb@resto@set{\boxmaxdepth\the\boxmaxdepth
+ \splittopskip\the\splittopskip}%
+ \boxmaxdepth\z@ \splittopskip\z@
+ \setbox\tw@\vsplit\@tempboxa to\dimen@
+ \setbox\tw@\vbox{\unvbox\tw@}% natural-sized
+| \message{Box of size \the\ht\@tempboxa\space split to \the\dimen@.
+| Natural height of split box is \the\ht\tw@. }%
+ % If the split-to size > (\vsize-\topskip), then set box to full size
+ \begingroup
+ \advance\dimen@\topskip
+ \expandafter\endgroup
+ \ifdim\dimen@>\pagegoal
+| \message{Frame is big -- Use up the full column. }%
+ \dimen@ii\pagegoal
+ \advance\dimen@ii -\topskip
+ \advance\dimen@ii \exam@frameHeightAdjust\relax
+ \else % suspect this is wrong:
+ % If the split-to size > feasible room_on_page, rebox it smaller.
+ \advance\dimen@.8\pageshrink
+ \ifdim\ht\tw@>\dimen@
+| \message{Box too tall; rebox it to \the\dimen@. }%
+ \dimen@ii\dimen@
+ \else % use natural size
+ \dimen@ii\ht\tw@
+ \fi
+ \fi
+ % Re-box contents to desired size \dimen@ii
+ \advance\dimen@ii -\exam@fb@frh
+ \setbox\tw@\vbox to\dimen@ii \bgroup
+ % remove simulated frame and page flexibility:
+ \vskip -\exam@fb@frh \@plus-\pagestretch \@minus-.8\pageshrink
+ \unvbox\tw@ \unpenalty\unpenalty
+ \ifdim\lastkern=-137sp % whole box went to next page
+| \message{box split at beginning! }%
+ \egroup \exam@fb@resto@set \eject % (\vskip for frame size
+ % was discarded)
+ \exam@fb@adjheight
+ \else %
+ \egroup \exam@fb@resto@set
+ \ifvoid\@tempboxa % it all fit after all
+| \message{box split at end! }%
+ \setbox\@tempboxa\box\tw@
+ \else % it really did split
+| \message{box split as expected. Its reboxed height is \the\ht\tw@. }%
+ \ifdim\wd\tw@>\z@
+%psh: Changed the command that inserts the box:
+% Instead of \centerline, we shift right by \saved@totalleftmargin:
+% \centerline{\exam@FrameCommand{\box\tw@}}% ??? \centerline bad idea
+ \hbox{\hskip \saved@totalleftmargin\exam@FrameCommand{\box\tw@}}%
+ \else
+| \message{Zero width means likely blank. Don't frame it (guess)}%
+ \box\tw@
+ \fi
+ \hrule \@height\z@
+ \eject
+ \exam@fb@adjheight
+ \exam@put@frame
+ \fi\fi\fi\fi\fi
+ \ifvoid\@tempboxa\else
+%psh: Changed the command that inserts the box:
+% Instead of \centerline, we shift right by \saved@totalleftmargin:
+% \centerline{\exam@FrameCommand{\box\@tempboxa}}%
+ \hbox{\hskip\saved@totalleftmargin\exam@FrameCommand{\box\@tempboxa}}%
+ \nointerlineskip \null %{\showoutput \showlists}
+ \penalty-30 \vskip\topsep
+ \fi}
+
+\def\exam@fb@adjheight{%
+ \vbox to\exam@frameHeightAdjust{}% get proper baseline skip from above.
+ \penalty\@M \nointerlineskip
+ \vskip-\exam@frameHeightAdjust
+ \penalty\@M} % useful for tops of pages
+
+\edef\exam@zero@glue{\the\z@skip}
+
+\catcode`\|=\exam@FrameRestore
+
+% Provide configuration commands:
+%psh: Version 2.502, 2016/03/23, changed \exam@FrameCommand so that the
+% frame is printed in color if the user has said
+% \colorsolutionboxes:
+%\providecommand\exam@FrameCommand{\fboxrule=\exam@FrameRule
+%\fboxsep=\exam@FrameSep \fbox}
+\def\exam@FrameCommand{\fboxrule=\exam@FrameRule \fboxsep=\exam@FrameSep
+ \if@colorsolutionboxes
+ \def\box@it{\colorfbox{SolutionBoxColor}}%
+ \else
+ \def\box@it{\fbox}%
+ \fi
+ \box@it
+}% \exam@FrameCommand
+
+\@ifundefined{FrameRule}{\newdimen\exam@FrameRule \exam@FrameRule=\fboxrule}{}
+\@ifundefined{FrameSep} {\newdimen\exam@FrameSep \exam@FrameSep =3\fboxsep}{}
+
+% Height of frame above first baseline when frame starts a page:
+\providecommand\exam@frameHeightAdjust{6pt}
+
+% \exam@FrameRestore has parts of \@parboxrestore. See how it is used in the
+% "settings" argument of \MakeFrame. Previous behavior can be restored by
+% using \@parboxrestore there, or redefining:
+% \makeatletter \renewcommand\exam@FrameRestore{\@parboxrestore} \makeatother
+\def\exam@FrameRestore{%
+ \let\if@nobreak\iffalse
+ \let\if@noskipsec\iffalse
+% \let\par\@@par ??
+ \let\-\@dischyph
+ \let\'\@acci\let\`\@accii\let\=\@acciii
+% \parindent\z@ \parskip\z@skip Definitely omit!
+% \everypar{}% ??
+ \linewidth\hsize
+% \@totalleftmargin\z@
+% \leftskip\z@skip \rightskip\z@skip \@rightskip\z@skip
+% \parfillskip\@flushglue \lineskip\normallineskip
+% \baselineskip\normalbaselineskip
+ \sloppy
+% \let\\\@normalcr
+}
+
+% Compatibility with previous versions (temporary!):
+% psh: we'll remove this 2017-09-21
+%\let\fram@d=\MakeFramed \let\endfram@d=\endMakeFramed
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+% This ends the stuff that's lifted from:
+% % framed.sty v 0.8a 21-Jul-2003
+% % Copyright (C) 1992-2003 by Donald Arseneau
+
+
+%--------------------------------------------------------------------
+%--------------------------------------------------------------------
+
+\endinput
+%---------------------------------------------------------------------
+%---------------------------------------------------------------------
+%---------------------------------------------------------------------
+%---------------------------------------------------------------------
+%---------------------------------------------------------------------