Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@nathanesau
Created August 3, 2015 19:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nathanesau/5555f4d66d7fee77bb59 to your computer and use it in GitHub Desktop.
Save nathanesau/5555f4d66d7fee77bb59 to your computer and use it in GitHub Desktop.
Exam Class (modified to allow hiding questions)
% exam2.cls
%
% A LaTeX2e document class for preparing exams.
%% exam.cls
%% Copyright (c) 1994, 1997, 2000, 2004, 2008, 2011, 2015 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.5}
\def\filedate{2015/05/07}
%---------------------------------------------------------------------
%---------------------------------------------------------------------
%
% 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.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{exam2}[\filedate\space Version \fileversion\space by
Philip Hirschhorn]
\RequirePackage{ifthen}
\newif\ifprintanswers
\printanswersfalse
\DeclareOption{answers}{\printanswerstrue}
\DeclareOption{noanswers}{\printanswersfalse}
\newif\ifprintquestions
\printquestionsfalse
\DeclareOption{yesquestions}{\printquestionstrue}
\DeclareOption{noquestions}{\printquestionsfalse}
\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
% 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
% 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
}
\def\@setfootheight#1{%
\begingroup % Avoid trouble from using \@temp and \@spaces
% Reset the effect of the most recent change:
\global\advance\textheight 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
% 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
}
%---------------------------------------------------------------------
%
% *************************
% ** 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\bf ??}}%
\exam@lastpage
}% numpages
% Change 2011/04/01: We added a ``0'' in front of the
% mbox when \exam@lastcoverpage isn't defined. This is
% so that the construction \romannumeral\numcoverpages
% won't generate an error on the first run of latex.
\def\numcoverpages{\@ifundefined{exam@lastcoverpage}%
{0\mbox{\normalfont\bf ??}}%
\exam@lastcoverpage
}% numpages
\def\totalnumpages{\@ifundefined{exam@totalpages}%
{\mbox{\normalfont\bf ??}}%
\exam@totalpages
}% numpages
\def\numpoints{\@ifundefined{exam@numpoints}%
{\mbox{\normalfont\bf ??}}%
\exam@numpoints
}% numpoints
\def\numbonuspoints{\@ifundefined{exam@numbonuspoints}%
{\mbox{\normalfont\bf ??}}%
\exam@numbonuspoints
}% numbonuspoints
\def\numquestions{\@ifundefined{exam@numquestions}%
{\mbox{\normalfont\bf ??}}%
\exam@numquestions
}% numquestions
\def\numparts{\@ifundefined{exam@numparts}%
{\mbox{\normalfont\bf ??}}%
\exam@numparts
}% numparts
\def\numsubparts{\@ifundefined{exam@numsubparts}%
{\mbox{\normalfont\bf ??}}%
\exam@numsubparts
}% numsubparts
\def\numsubsubparts{\@ifundefined{exam@numsubsubparts}%
{\mbox{\normalfont\bf ??}}%
\exam@numsubsubparts
}% numsubsubparts
\def\pointsofquestion#1{\@ifundefined{pointsofq@\romannumeral #1}%
{\mbox{\normalfont\bf ??}}%
{\csname pointsofq@\romannumeral #1\endcsname}%
}% pointsofquestion
\def\bonuspointsofquestion#1{\@ifundefined{bonuspointsofq@\romannumeral #1}%
{\mbox{\normalfont\bf ??}}%
{\csname bonuspointsofq@\romannumeral #1\endcsname}%
}% bonuspointsofquestion
% For use in \combinedgradetable and \combinedpointtable, we're
% changing the defintions of \pointsonpage and \bonuspointsonpage
% so that when, e.g., pointsonpage@ii is undefined, we just produce
% 0. This comes up because the final page of the exam may have either
% points with no bonus point or bonus points with no points, in which
% case the combined table will try to list both points and bonus points
% for that last page, and one of those will be undefined.
%
% \def\pointsonpage#1{\@ifundefined{pointsonpage@\romannumeral #1}%
% {\mbox{\normalfont\bf ??}}%
% {\csname pointsonpage@\romannumeral #1\endcsname}%
% }% pointsonpage
% \def\bonuspointsonpage#1{\@ifundefined{bonuspointsonpage@\romannumeral #1}%
% {\mbox{\normalfont\bf ??}}%
% {\csname bonuspointsonpage@\romannumeral #1\endcsname}%
% }% bonuspointsonpage
%
% spanish.ldf redefines \@roman, so we'll avoid using \roman:
\def\pointsonpage#1{\@ifundefined{pointsonpage@\romannumeral #1}%
{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
\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\@temp{#1}%
\ifx\@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@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
\def\pointsinleftmargin{\global\@pointsinleftmargintrue
\global\@pointsinrightmarginfalse
\global\@pointsdroppedfalse
\gdef\pt@name{\@marginpointname}%
\gdef\bnspt@name{\@marginbonuspointname}}
\def\pointsinrightmargin{\global\@pointsinrightmargintrue
\global\@pointsinleftmarginfalse
\global\@pointsdroppedfalse
\gdef\pt@name{\@marginpointname}%
\gdef\bnspt@name{\@marginbonuspointname}}
\def\nopointsinmargin{\global\@pointsinleftmarginfalse
\global\@pointsinrightmarginfalse
\global\@pointsdroppedfalse
\gdef\pt@name{\@pointname}%
\gdef\bnspt@name{\@bonuspointname}}
\def\pointsdroppedatright{\global\@pointsdroppedtrue
\global\@pointsinleftmarginfalse
\global\@pointsinrightmarginfalse
\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}%
}
\def\thebonuspoints{\@points \@bonuspointname}
%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
\endgroup
\fi
\item
\do@choice@pageinfo
} % choice
\def\CorrectChoice{%
\if@correctchoice
\endgroup
\fi
\ifprintanswers
% We can't say \choice here, because that would
% insert an \endgroup:
\begingroup \@correctchoicetrue
\CorrectChoice@Emphasis
\fi
\item
\do@choice@pageinfo
} % CorrectChoice
\let\correctchoice\CorrectChoice
\labelwidth\leftmargin\advance\labelwidth-\labelsep
\topsep=0pt
\partopsep=0pt
\choiceshook
}%
}%
{\if@correctchoice \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
\endgroup
\fi
\item
\do@choice@pageinfo
} % choice
\def\CorrectChoice{%
\if@correctchoice
\endgroup
\fi
\ifprintanswers
% We can't say \choice here, because that would
% insert an \endgroup:
\begingroup \@correctchoicetrue
\CorrectChoice@Emphasis
\item[\checked@char]
\else
\item
\fi
\do@choice@pageinfo
} % CorrectChoice
\let\correctchoice\CorrectChoice
\labelwidth\leftmargin\advance\labelwidth-\labelsep
\topsep=0pt
\partopsep=0pt
\checkboxeshook
}%
}%
{\if@correctchoice \endgroup \fi \endlist}
\newenvironment{oneparcheckboxes}%
{%
% Although we're not printing numbers for the choices, we use the
% choice counter to keep track of whether a choice is the first
% one (in which case we don't leave any additional space) or a
% later one (in which case we do leave additional space):
\setcounter{choice}{0}%
\def\choice{%
\if@correctchoice \endgroup \fi
\stepcounter{choice}%
\ifnum\value{choice}>1\relax
\penalty -50\hskip 1em plus 1em\relax
\fi
\checkbox@char
% No need to put the following into a token string; we just put
% \checkbox@char onto the page, so we're at the spot whose page
% number we want to record:
\questionobject@pluspagecheck
\nobreak\enskip
}% choice
\def\CorrectChoice{%
\if@correctchoice \endgroup \fi
\stepcounter{choice}%
\ifprintanswers
\begingroup \@correctchoicetrue
\CorrectChoice@Emphasis
\fi
\ifnum\value{choice}>1\relax
\penalty -50\hskip 1em plus 1em\relax
\fi
\ifprintanswers
\checked@char
\else
\checkbox@char
\fi
% No need to put the following into a token string; we just put
% the choicelabel onto the page, so we're at the spot whose page
% number we want to record:
\questionobject@pluspagecheck
\nobreak\enskip
}% CorrectChoice
\let\correctchoice\CorrectChoice
\let\par\@empty
% If we're continuing the paragraph containing the question,
% then leave a bit of space before the first choice:
\ifvmode\else\enskip\fi
\ignorespaces
}%
{\if@correctchoice \endgroup \fi}
%--------------------------------------------------------------------
% Answer Lines (for short answer questions)
% Note: \ques@ref is also used in \item@points@pageinfo
\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{%
\CorrectChoice@Emphasis \hfil #1\hss}%
\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{\CorrectChoice@Emphasis \fillin@ans}%
\else
\hbox to #1{\CorrectChoice@Emphasis \hfil \fillin@ans \hfil}%
\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.
\newlength\linefillheight
\newlength\linefillthickness
\setlength\linefillheight{.25in}
\setlength\linefillthickness{0.1pt}
\newcommand\linefill{\leavevmode
\leaders\hrule height \linefillthickness \hfill\kern\z@}
\def\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
}
%--------------------------------------------------------------------
% \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.
\newlength\dottedlinefillheight
\setlength\dottedlinefillheight{.25in}
\def\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
}% 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}
\newcommand\makeemptybox[1]{%
\par
\hbox to \hsize{\hskip\@totalleftmargin \leaders\hrule\hfill}%
\nointerlineskip
\begingroup
\setbox0=\hbox to \hsize{\hskip\@totalleftmargin
\vrule height\minboxheight \hfill \vrule}%
% The vertical size desired may not be an exact multiple of
% \minboxheight, and so \cleaders might leave a gap between the
% vertical lines and the horizontal lines above and below it.
% Thus, we put a single copy of \box0 immediately below the
% horizontal line above and we'll also put a single copy of \box0
% immediately above the horizontal line below.
\copy0
\nobreak
\vskip -\minboxheight
\cleaders \copy0 \vskip #1
\vskip -\minboxheight
\nointerlineskip
\copy0
\endgroup
\nointerlineskip
\hbox to \hsize{\hskip\@totalleftmargin \leaders\hrule\hfill}%
}
%--------------------------------------------------------------------
% \uplevel and \fullwidth
% and the EnvUplevel and EnvFullwidth environments:
% \uplevel is used to print text at the indentation level of the
% enclosing environment. For example, to precede a question with
% 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:}
% The only commands here accessible to the user are \gradetable,
% \bonusgradetable, \combinedgradetable, \pointtable,
% \bonuspointtable, \combinedpointtable, \partialgradetable,
% \partialbonusgradetable, \partialcombinedtable, \partialpointtable,
% \partialbonuspointtable, \partialcombinedpointtable,
% \begingradingrange, \endgradingrange, \pointsinrange,
% \bonuspointsinrange, \firstqinrange, \lastqinrange, and
% \numqinrange.
% 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.
% 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
% or \combinedpointtable:
\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}
\def\partialgradetable#1{%
\@scorestrue
\@bonusfalse
\@partialtrue
\@combinedfalse
\def\tbl@range{#1}%
% If the user doesn't include the optional argument
% choosing between vertical and horizontal,
% we give them vertical:
\@ifnextchar[{\i@gtable}{\i@gtable[v]}%
}% partialgradetable
\def\partialbonusgradetable#1{%
\@scorestrue
\@bonustrue
\@partialtrue
\@combinedfalse
\def\tbl@range{#1}%
% If the user doesn't include the optional argument
% choosing between vertical and horizontal,
% we give them vertical:
\@ifnextchar[{\i@gtable}{\i@gtable[v]}%
}% partialbonusgradetable
\def\partialcombinedgradetable#1{%
\@scorestrue
\@bonusfalse
\@partialtrue
\@combinedtrue
\def\tbl@range{#1}%
% If the user doesn't include the optional argument
% choosing between vertical and horizontal,
% we give them vertical:
\@ifnextchar[{\i@gtable}{\i@gtable[v]}%
}% partialcombinedgradetable
\def\partialpointtable#1{%
\@scoresfalse
\@bonusfalse
\@partialtrue
\@combinedfalse
\def\tbl@range{#1}%
% If the user doesn't include the optional argument
% choosing between vertical and horizontal,
% we give them vertical:
\@ifnextchar[{\i@gtable}{\i@gtable[v]}%
}% partialpointtable
\def\partialbonuspointtable#1{%
\@scoresfalse
\@bonustrue
\@partialtrue
\@combinedfalse
\def\tbl@range{#1}%
% If the user doesn't include the optional argument
% choosing between vertical and horizontal,
% we give them vertical:
\@ifnextchar[{\i@gtable}{\i@gtable[v]}%
}% partialbonuspointtable
\def\partialcombinedpointtable#1{%
\@scoresfalse
\@bonusfalse
\@partialtrue
\@combinedtrue
\def\tbl@range{#1}%
% If the user doesn't include the optional argument
% choosing between vertical and horizontal,
% we give them vertical:
\@ifnextchar[{\i@gtable}{\i@gtable[v]}%
}% partialcombinedpointtable
%--------------------------------------------------------------------
\def\gradetable{%
\@scorestrue
\@bonusfalse
\@partialfalse
\@combinedfalse
% If the user doesn't include the optional argument
% choosing between vertical and horizontal,
% we give them vertical:
\@ifnextchar[{\i@gtable}{\i@gtable[v]}%
}% gradetable
\def\bonusgradetable{%
\@scorestrue
\@bonustrue
\@partialfalse
\@combinedfalse
% If the user doesn't include the optional argument
% choosing between vertical and horizontal,
% we give them vertical:
\@ifnextchar[{\i@gtable}{\i@gtable[v]}%
}% bonusgradetable
\def\combinedgradetable{%
\@scorestrue
\@bonusfalse
\@partialfalse
\@combinedtrue
% If the user doesn't include the optional argument
% choosing between vertical and horizontal,
% we give them vertical:
\@ifnextchar[{\i@gtable}{\i@gtable[v]}%
}% bonusgradetable
\def\pointtable{%
\@scoresfalse
\@bonusfalse
\@partialfalse
\@combinedfalse
% If the user doesn't include the optional argument
% choosing between vertical and horizontal,
% we give them vertical:
\@ifnextchar[{\i@gtable}{\i@gtable[v]}%
}% pointtable
\def\bonuspointtable{%
\@scoresfalse
\@bonustrue
\@partialfalse
\@combinedfalse
% If the user doesn't include the optional argument
% choosing between vertical and horizontal,
% we give them vertical:
\@ifnextchar[{\i@gtable}{\i@gtable[v]}%
}% bonuspointtable
\def\combinedpointtable{%
\@scoresfalse
\@bonusfalse
\@partialfalse
\@combinedtrue
% If the user doesn't include the optional argument
% choosing between vertical and horizontal,
% we give them vertical:
\@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.
% \do@table 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 make sure the user said \addpoints, and then make sure
% that this isn't the first run of LaTeX (by checking that
% \exam@numpoints is defined). If both of those are OK,
% we go on to \do@table to see whether we're doing a table
% 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}%
}%
{\do@table{#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\do@table#1#2{%
% The first argument is ``v'' or ``h'';
% the second argument is ``questions'' or ``pages''.
% See whether we're doing a table indexed by
% questions or by pages:
\begingroup % avoid trouble from using \@temp
\def\@temp{#2}%
\ifx\@temp\@questionsref
\find@qrange{#1}%
\else
\ifx\@temp\@pagesref
\find@prange{#1}%
\else
\ClassError{exam}{%
The second optional argument to a\MessageBreak
\space\space grade table or point table command\MessageBreak
\space \space must be either `questions' or
`pages',\MessageBreak
\space\space not `#2'.\MessageBreak
}{%
Grade tables and point tables can be indexed by questions or
pages;\MessageBreak
\space\space for others, you're on your own.\MessageBreak
}%
\fbox{Error: grade or point table: Invalid second
optional argument `#2'.}%
\fi
\fi
\endgroup
}% do@table
\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:
% We get to \find@qrange if we know we're doing a table indexed
% by question numbers. The argument is either ``v'' or ``h''.
% If we're not doing a partial table, then \find@qrange sets
% \tbl@firstq to 1 and \tbl@lastq to \numquestions. Otherwise,
% \find@qrange makes sure the grading range is defined and that
% its last question isn't before its first question.
% \find@qrange then calls \@tblquestions, passing along the argument
% that is either ``v'' or ``h''.
\def\find@qrange#1{%
% 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}%
% Check that firstq precedes or equals lastq:
\ifnum \tbl@firstq > \tbl@lastq\relax
\fbox{Error: Grading Range `\tbl@range ':
Last question precedes first question.}%
\ClassError{exam}{%
In grading range `\tbl@range ',
the last question\MessageBreak
\space\space comes before the first question.\MessageBreak
}{%
\string\begingradingrange \space must precede
\string\endgradingrange \space by at
least one question.\MessageBreak
}%
\else
\@tblquestions{#1}%
\fi
}%
}%
\else
\def\tbl@firstq{1}%
% \numquestions is always defined, even if this is the first
% run of LaTeX and \exam@numquestions isn't defined.
% If it's the first run of LaTeX, then its value isn't useful,
% but it's never used until a later run (when its value is useful).
\def\tbl@lastq{\numquestions}%
\@tblquestions{#1}%
\fi
}% find@qrange
\def\@tblquestions#1{%
% \tbl@firstq and \tbl@lastq have already been set.
% The argument is either ``v'' or ``h', and we branch accordingly.
\if v#1%
\@vtblquestions
\else
\if h#1%
\@htblquestions
\else
\ClassError{exam}{%
The first optional argument to \protect\gradetable,\MessageBreak
\space\space \protect\bonusgradetable,
\protect\pointtable, or\MessageBreak
\space\space \protect\bonuspointtable
\space must be either `h' or `v'\MessageBreak
}{%
Grade tables and point tables can be either horizontal or
vertical;\MessageBreak
\space\space no diagonals allowed.\MessageBreak
}%
\if@scores
\if@bonus
\fbox{Error: bonusgradetable: Invalid first optional argument
`#1'.}%
\else
\fbox{Error: gradetable: Invalid first optional argument
`#1'.}%
\fi
\else
\if@bonus
\fbox{Error: bonuspointtable: Invalid first optional argument
`#1'.}%
\else
\fbox{Error: pointtable: Invalid first optional argument
`#1'.}%
\fi
\fi
\fi
\fi
}% @tblquestions
%--------------------------------------------------------------------
% Vertical, indexed by question numbers:
\def\@vtblquestions{%
% The table is vertical and indexed by question numbers.
% The question range has already been determined.
% Branch according to whether it's bonus, non-bonus, or combined,
% and gradetable or pointtable:
\begingroup
% Save the current value of question in @iterator, so that
% we can restore it after doing the table:
\setcounter{@iterator}{\value{question}}%
\renewcommand\arraystretch{\@gtblstretch}%
\if@bonus
\if@scores
\@bvgrdtblquestions
\else
\@bvpttblquestions
\fi
\else
\if@combined
\if@scores
\@cvgrdtblquestions
\else
\@cvpttblquestions
\fi
\else
\if@scores
\@vgrdtblquestions
\else
\@vpttblquestions
\fi
\fi
\fi
% Restore the saved value of question:
\setcounter{question}{\value{@iterator}}%
\endgroup
}% @vtblquestions
\def\@cvgrdtblquestions{%
% Vertical combined gradetable, indexed by questions
\set@hlfcntr{tbl@points}{0}%
\set@hlfcntr{tbl@bonuspoints}{0}%
\begin{tabular}{|c|c|c|c|}
\hline
{\@cvqword}& {\@cvpword}& {\@cvbpword}& {\@cvsword}\\
\hline
\setcounter{question}{\tbl@firstq}%
\addtocounter{question}{-1}\do@cvloop
{\@cvtword}& \prt@tablepoints&
\prt@tablebonuspoints&
\hbox to \@cellwidth{\hfill}\\
\hline
\end{tabular}%
}
\def\do@cvloop{%
% \do@cvloop is used by \@cvgrdtblquestions
\addtocounter{question}{1}%
\ref{question@\arabic{question}} &
\pointsofquestion{\arabic{question}}&
\bonuspointsofquestion{\arabic{question}}&\\
\hline
\@ifundefined{pointsofq@\romannumeral \c@question}%
{}%
{\addto@hlfcntr{tbl@points}
{\csname pointsofq@\romannumeral \c@question\endcsname}}%
\@ifundefined{bonuspointsofq@\romannumeral \c@question}%
{}%
{\addto@hlfcntr{tbl@bonuspoints}
{\csname bonuspointsofq@\romannumeral \c@question\endcsname}}%
\ifnum \value{question} < \tbl@lastq\relax
\let\next@cvloop=\do@cvloop
\else
\let\next@cvloop=\relax
\fi
\next@cvloop
}% do@cvloop
\def\@cvpttblquestions{%
% Vertical combined pointtable, indexed by questions
\set@hlfcntr{tbl@points}{0}%
\set@hlfcntr{tbl@bonuspoints}{0}%
\begin{tabular}{|c|c|c|}
\hline
{\@cvqword}& {\@cvpword}& {\@cvbpword}\\
\hline
\setcounter{question}{\tbl@firstq}%
\addtocounter{question}{-1}\do@cvptloop
{\@cvtword}& \prt@tablepoints&
\prt@tablebonuspoints\\
\hline
\end{tabular}%
}
\def\do@cvptloop{%
% \do@cvptloop is used by \@cvpttblquestions
\addtocounter{question}{1}%
\ref{question@\arabic{question}} &
\pointsofquestion{\arabic{question}}&
\bonuspointsofquestion{\arabic{question}}\\
\hline
\@ifundefined{pointsofq@\romannumeral \c@question}%
{}%
{\addto@hlfcntr{tbl@points}
{\csname pointsofq@\romannumeral \c@question\endcsname}}%
\@ifundefined{bonuspointsofq@\romannumeral \c@question}%
{}%
{\addto@hlfcntr{tbl@bonuspoints}
{\csname bonuspointsofq@\romannumeral \c@question\endcsname}}%
\ifnum \value{question} < \tbl@lastq\relax
\let\next@cvptloop=\do@cvptloop
\else
\let\next@cvptloop=\relax
\fi
\next@cvptloop
}% do@cvptloop
\def\@bvgrdtblquestions{%
% Vertical bonus gradetable, indexed by questions:
\set@hlfcntr{tbl@bonuspoints}{0}%
\begin{tabular}{|c|c|c|}
\hline
{\@bvqword}& {\@bvpword}& {\@bvsword}\\
\hline
\setcounter{question}{\tbl@firstq}%
\addtocounter{question}{-1}\do@bvloop
{\@bvtword}& \prt@tablebonuspoints&\hbox to \@cellwidth{\hfill}\\
\hline
\end{tabular}%
}% @bvgrdtblquestions
\def\do@bvloop{%
% \do@bvloop is used by \@bvgrdtblquestions
\addtocounter{question}{1}%
\ref{question@\arabic{question}} &
\bonuspointsofquestion{\arabic{question}}&\\
\hline
\@ifundefined{bonuspointsofq@\romannumeral \c@question}%
{}%
{\addto@hlfcntr{tbl@bonuspoints}
{\csname bonuspointsofq@\romannumeral \c@question\endcsname}}%
\ifnum \value{question} < \tbl@lastq\relax
\let\next@bvloop=\do@bvloop
\else
\let\next@bvloop=\relax
\fi
\next@bvloop
}% do@bvloop
\def\@bvpttblquestions{%
% Vertical bonus point table, indexed by questions:
\set@hlfcntr{tbl@bonuspoints}{0}%
\begin{tabular}{|c|c|}
\hline
{\@bvqword}& {\@bvpword}\\
\hline
\setcounter{question}{\tbl@firstq}%
\addtocounter{question}{-1}\do@bvptloop
{\@bvtword}& \prt@tablebonuspoints\\
\hline
\end{tabular}%
}% @bvpttblquestions
\def\do@bvptloop{%
% \do@bvptloop is used by \@bvpttblquestions
\addtocounter{question}{1}%
\ref{question@\arabic{question}} &
\bonuspointsofquestion{\arabic{question}}\\
\hline
\@ifundefined{bonuspointsofq@\romannumeral \c@question}%
{}%
{\addto@hlfcntr{tbl@bonuspoints}
{\csname bonuspointsofq@\romannumeral \c@question\endcsname}}%
\ifnum \value{question} < \tbl@lastq\relax
\let\next@bvptloop=\do@bvptloop
\else
\let\next@bvptloop=\relax
\fi
\next@bvptloop
}% do@bvptloop
\def\@vgrdtblquestions{%
% Vertical non-bonus grade table, indexed by questions:
\set@hlfcntr{tbl@points}{0}%
\begin{tabular}{|c|c|c|}
\hline
{\@vqword}& {\@vpword}& {\@vsword}\\
\hline
\setcounter{question}{\tbl@firstq}%
\addtocounter{question}{-1}\do@vloop
{\@vtword}& \prt@tablepoints&\hbox to \@cellwidth{\hfill}\\
\hline
\end{tabular}%
}% @vgrdtblquestions
\def\do@vloop{%
% \do@vloop is used by \@vgrdtblquestions
\addtocounter{question}{1}%
\ref{question@\arabic{question}} &
\pointsofquestion{\arabic{question}}&\\
\hline
\@ifundefined{pointsofq@\romannumeral \c@question}%
{}%
{\addto@hlfcntr{tbl@points}
{\csname pointsofq@\romannumeral \c@question\endcsname}}%
\ifnum \value{question} < \tbl@lastq\relax
\let\next@vloop=\do@vloop
\else
\let\next@vloop=\relax
\fi
\next@vloop
}% do@vloop
\def\@vpttblquestions{%
% Vertical non-bonus point table, indexed by questions:
\set@hlfcntr{tbl@points}{0}%
\begin{tabular}{|c|c|}
\hline
{\@vqword}& {\@vpword}\\
\hline
\setcounter{question}{\tbl@firstq}%
\addtocounter{question}{-1}\do@vptloop
{\@vtword}& \prt@tablepoints\\
\hline
\end{tabular}%
}% @vpttblquestions
\def\do@vptloop{%
% \do@vptloop is used by \@vpttblquestions
\addtocounter{question}{1}%
\ref{question@\arabic{question}} &
\pointsofquestion{\arabic{question}}\\
\hline
\@ifundefined{pointsofq@\romannumeral \c@question}%
{}%
{\addto@hlfcntr{tbl@points}
{\csname pointsofq@\romannumeral \c@question\endcsname}}%
\ifnum \value{question} < \tbl@lastq\relax
\let\next@vptloop=\do@vptloop
\else
\let\next@vptloop=\relax
\fi
\next@vptloop
}% do@vptloop
%--------------------------------------------------------------------
% Horizontal, indexed by question numbers:
\def\@htblquestions{%
% We get here from \@tblquestions.
% The table is horizontal and indexed by question numbers.
% The question range has already been determined.
% Set num@cols equal to the number of questions on the table, and
% do either bonus, combined, or non-bonus table, in each case
% putting in a line for scores only if it's a gradetable:
\@ifundefined{exam@numquestions}%
{}%
{%
\setcounter{num@cols}{\tbl@lastq}%
\addtocounter{num@cols}{-\tbl@firstq}%
\addtocounter{num@cols}{1}%
}%
% Do either bonus, combined, or non-bonus table. In each case, put
% in a line for scores if it's a gradetable:
\begingroup
% Save the current value of question in @iterator, so that
% we can restore it after doing the table:
\setcounter{@iterator}{\value{question}}%
\renewcommand\arraystretch{\@gtblstretch}%
\if@bonus
% It's a horizontal bonus table, by questions:
\begin{tabular}{|l|*{\thenum@cols}{c|}c|}
\hline
{\@bhqword}& \setcounter{question}{\tbl@firstq}%
\addtocounter{question}{-1}\do@qnumloop
{\@bhtword}\\
\hline
\set@hlfcntr{tbl@bonuspoints}{0}%
{\@bhpword}& \setcounter{question}{\tbl@firstq}%
\addtocounter{question}{-1}\do@bptloop
\prt@tablebonuspoints\\
\hline
% If it's a grade table, add in the score line:
\if@scores
{\@bhsword}& \setcounter{question}{\tbl@firstq}%
\addtocounter{question}{-1}\do@sloop
\\
\hline
\fi
\end{tabular}%
\else
\if@combined
% It's a horizontal combined table, by questions:
\begin{tabular}{|l|*{\thenum@cols}{c|}c|}
\hline
{\@chqword}& \setcounter{question}{\tbl@firstq}%
\addtocounter{question}{-1}\do@qnumloop
{\@chtword}\\
\hline
\set@hlfcntr{tbl@points}{0}%
{\@chpword}& \setcounter{question}{\tbl@firstq}%
\addtocounter{question}{-1}\do@ptloop
\prt@tablepoints\\
\hline
\set@hlfcntr{tbl@bonuspoints}{0}%
{\@chbpword}& \setcounter{question}{\tbl@firstq}%
\addtocounter{question}{-1}\do@bptloop
\prt@tablebonuspoints\\
\hline
% If it's a grade table, add in the score line:
\if@scores
{\@chsword}& \setcounter{question}{\tbl@firstq}%
\addtocounter{question}{-1}\do@sloop
\\
\hline
\fi
\end{tabular}%
\else
% Horizontal non-bonus table, indexed by question number:
\set@hlfcntr{tbl@points}{0}%
\begin{tabular}{|l|*{\thenum@cols}{c|}c|}
\hline
{\@hqword}& \setcounter{question}{\tbl@firstq}%
\addtocounter{question}{-1}\do@qnumloop
{\@htword}\\
\hline
{\@hpword}& \setcounter{question}{\tbl@firstq}%
\addtocounter{question}{-1}\do@ptloop
\prt@tablepoints\\
\hline
% If it's a grade table, add in the score line:
\if@scores
{\@hsword}& \setcounter{question}{\tbl@firstq}%
\addtocounter{question}{-1}\do@sloop
\\
\hline
\fi
\end{tabular}%
\fi
\fi
% Restore the saved value of question:
\setcounter{question}{\value{@iterator}}%
\endgroup
}% @htblquestions
% \do@qnumloop and \do@sloop are used by non-bonus,
% combined, and bonus tables:
\def\do@qnumloop{%
\addtocounter{question}{1}%
\ref{question@\arabic{question}}&
\ifnum \value{question} < \tbl@lastq\relax
\let\next@qnloop=\do@qnumloop
\else
\let\next@qnloop=\relax
\fi
\next@qnloop
}% do@qnumloop
\def\do@sloop{%
\addtocounter{question}{1}%
\hbox to \@cellwidth{\hfill}&
\ifnum \value{question} < \tbl@lastq\relax
\let\next@sloop=\do@sloop
\else
\let\next@sloop=\relax
\fi
\next@sloop
}% do@sloop
% \do@ptloop is used by combined and non-bonus tables:
\def\do@ptloop{%
\addtocounter{question}{1}%
\pointsofquestion{\arabic{question}}&
\@ifundefined{pointsofq@\romannumeral \c@question}%
{}%
{\addto@hlfcntr{tbl@points}
{\csname pointsofq@\romannumeral \c@question\endcsname}}%
\ifnum \value{question} < \tbl@lastq\relax
\let\next@ptloop=\do@ptloop
\else
\let\next@ptloop=\relax
\fi
\next@ptloop
}% do@ptloop
% \do@bptloop is used by bonus and combined tables:
\def\do@bptloop{%
\addtocounter{question}{1}%
\bonuspointsofquestion{\arabic{question}}&
\@ifundefined{bonuspointsofq@\romannumeral \c@question}%
{}%
{\addto@hlfcntr{tbl@bonuspoints}
{\csname bonuspointsofq@\romannumeral \c@question\endcsname}}%
\ifnum \value{question} < \tbl@lastq\relax
\let\next@bptloop=\do@bptloop
\else
\let\next@bptloop=\relax
\fi
\next@bptloop
}% do@bptloop
%--------------------------------------------------------------------
%--------------------------------------------------------------------
% 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
% \@tblpages.
\def\find@prange#1{%
% We're doing a table indexed by pages.
% The argument is either ``v'' or ``h''.
% After determining the first and last page of the range, we
% call \@tblpages, passing it that argument (i.e., we say
% \@tblpages{#1}).
% If not a partial table, we set \tbl@firstp to 1 and \tbl@lastp to
% the last page with the appropriate points (and so if it's a
% combined table, it's the last page to have either bonus or
% non-bonus points).
\if@partial
\@ifundefined{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}%
% Check that firstp precedes or equals lastp:
\ifnum \tbl@firstp > \tbl@lastp\relax
\fbox{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
\@tblpages{#1}%
\fi
}%
}%
\else
\def\tbl@firstp{1}%
% \tbl@lastp isn't used on the first run of LaTeX, and
% \lastpage@withbonuspoints is defined on the second and later runs.
\def\tbl@lastp{\lastpage@withpoints}%
\if@bonus
\def\tbl@lastp{\lastpage@withbonuspoints}%
\fi
\if@combined
\ifnum \lastpage@withbonuspoints > \lastpage@withpoints\relax
\def\tbl@lastp{\lastpage@withbonuspoints}%
\fi
\fi
\@tblpages{#1}%
\fi
}% find@prange
\def\@tblpages#1{%
% We get here from \find@prange; the argument is either ``v'' or
% ``h''.
% Check that there's enough info from the .aux file to do a page
% indexed grade table. If so, call \@whichtblpgs{#1}:
\@ifundefined{lastpage@withpoints}%
{\ClassWarning{exam}{%
You must run LaTeX twice more\MessageBreak
\space\space to produce the table.\MessageBreak}%
\fbox{Run \LaTeX{} twice more to produce the table}%
}%
{%
\@ifundefined{pointsonpage@\romannumeral
\csname lastpage@withpoints\endcsname}%
{\ClassWarning{exam}{%
You must run LaTeX again\MessageBreak
\space\space to produce the table.\MessageBreak}%
\fbox{Run \LaTeX{} again to produce the table}%
}%
{%
\@whchtblpgs#1
}%
}%
}% @tblpages
\def\@whchtblpgs#1{%
% At this point, we know the table is indexed by pages.
% It can be vertical or horizontal,
% grade or point, and bonus or non-bonus.
% The argument is either ``v'' or ``h''; test it, and branch:
\if v#1%
\@vtblpages
\else
\if h#1%
\@htblpages
\else
\ClassError{exam}{%
The first optional argument to
\protect\gradetable,\MessageBreak
\space\space \protect\bonusgradetable,
\protect\pointtable, or\MessageBreak
\space\space
\protect\bonuspointtable \space must be either `h' or
`v'\MessageBreak
}{%
Grade tables and point tables can be either horizontal or
vertical;\MessageBreak
\space\space no diagonals allowed.\MessageBreak
}%
\if@scores
\if@bonus
\fbox{Error: bonusgradetable: Invalid first optional argument
`#1'.}%
\else
\fbox{Error: gradetable: Invalid first optional argument
`#1'.}%
\fi
\else
\if@bonus
\fbox{Error: bonuspointtable: Invalid first optional argument
`#1'.}%
\else
\fbox{Error: pointtable: Invalid first optional argument
`#1'.}%
\fi
\fi
\fi
\fi
}% \@whchtblpgs
%--------------------------------------------------------------------
% Vertical, indexed by pages:
\def\@vtblpages{%
% We get here from \@whchtblpgs.
% At this point, we know it's a vertical table indexed by pages.
% It can be grade or point, and bonus, non-bonus, or combined.
\begingroup
\renewcommand\arraystretch{\@gtblstretch}%
\if@bonus
% It's vertical, bonus, by pages, either point or grade:
\set@hlfcntr{tbl@bonuspoints}{0}%
\if@scores
\begin{tabular}{|c|c|c|}
\hline
{\@bvpgword}& {\@bvpword}& {\@bvsword}\\
\hline
\setcounter{@iterator}{\tbl@firstp}%
\addtocounter{@iterator}{-1}\pg@bvloop
{\@bvtword}& \prt@tablebonuspoints&
\hbox to \@cellwidth{\hfill}\\
\hline
\end{tabular}%
\else
\begin{tabular}{|c|c|}
\hline
{\@bvpgword}& {\@bvpword}\\
\hline
\setcounter{@iterator}{\tbl@firstp}%
\addtocounter{@iterator}{-1}\pg@ptbvloop
{\@bvtword}& \prt@tablebonuspoints\\
\hline
\end{tabular}%
\fi
\else
\if@combined
% It's vertical, combined, by pages, either point or grade:
\set@hlfcntr{tbl@points}{0}%
\set@hlfcntr{tbl@bonuspoints}{0}%
\if@scores
% Combined gradetable, vertical, by pages:
\begin{tabular}{|c|c|c|c|}
\hline
{\@cvpgword}& {\@cvpword}& {\@cvbpword}& {\@cvsword}\\
\hline
\setcounter{@iterator}{\tbl@firstp}%
\addtocounter{@iterator}{-1}\pg@cvloop
{\@cvtword}& \prt@tablepoints&
\prt@tablebonuspoints&
\hbox to \@cellwidth{\hfill}\\
\hline
\end{tabular}%
\else
% Combined pointtable, vertical, by pages:
\begin{tabular}{|c|c|c|}
\hline
{\@cvpgword}& {\@cvpword}& {\@cvbpword}\\
\hline
\setcounter{@iterator}{\tbl@firstp}%
\addtocounter{@iterator}{-1}\pg@ptcvloop
{\@cvtword}& \prt@tablepoints&
\prt@tablebonuspoints\\
\hline
\end{tabular}%
\fi
\else
% It's non-bonus, either point or grade:
\set@hlfcntr{tbl@points}{0}%
\if@scores
\begin{tabular}{|c|c|c|}
\hline
{\@vpgword}& {\@vpword}& {\@vsword}\\
\hline
\setcounter{@iterator}{\tbl@firstp}%
\addtocounter{@iterator}{-1}\pg@vloop
{\@vtword}& \prt@tablepoints&\hbox to \@cellwidth{\hfill}\\
\hline
\end{tabular}%
\else
\begin{tabular}{|c|c|}
\hline
{\@vpgword}& {\@vpword}\\
\hline
\setcounter{@iterator}{\tbl@firstp}%
\addtocounter{@iterator}{-1}\pg@ptvloop
{\@vtword}& \prt@tablepoints\\
\hline
\end{tabular}%
\fi
\fi
\fi
\endgroup
}% @vtblpages
% \pg@ptvloop and \pg@ptvloopline are used only by non-bonus pointtable:
\def\pg@ptvloop{%
\addtocounter{@iterator}{1}%
\set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}%
\ifhlfcntr@pos{tmp@hlfcntr}%
\add@hlfcntrtohlfcntr{tbl@points}{tmp@hlfcntr}%
\pg@ptvloopline
\fi
\ifnum \the@iterator < \tbl@lastp\relax
\let\next@pg@ptvloop=\pg@ptvloop
\else
\let\next@pg@ptvloop=\relax
\fi
\next@pg@ptvloop
}% pg@ptvloop
\def\pg@ptvloopline{%
% We still don't understand why we need to hide this inside of a
% macro; there's some weird interaction with the \ifnum checking to
% see if something is ``>0''; checking that something is ``=0''
% doesn't cause the ``\ifnum incomplete'' (or whatever) error.
\pageref{firstpoints@onpage@\arabic{@iterator}} &
\pointsonpage{\the@iterator}\\
\hline
}% pg@ptvloopline
% \pg@vloop and \pg@vloopline are used only by non-bonus gradetable:
\def\pg@vloop{%
\addtocounter{@iterator}{1}%
\set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}%
\ifhlfcntr@pos{tmp@hlfcntr}%
\add@hlfcntrtohlfcntr{tbl@points}{tmp@hlfcntr}%
\pg@vloopline
\fi
\ifnum \the@iterator < \tbl@lastp\relax
\let\next@pg@vloop=\pg@vloop
\else
\let\next@pg@vloop=\relax
\fi
\next@pg@vloop
}% pg@vloop
\def\pg@vloopline{%
% We still don't understand why we need to hide this inside of a
% macro; there's some weird interaction with the \ifnum checking to
% see if something is ``>0''; checking that something is ``=0''
% doesn't cause the ``\ifnum incomplete'' (or whatever) error.
\pageref{firstpoints@onpage@\arabic{@iterator}} &
\pointsonpage{\the@iterator}&\\
\hline
}% pg@vloopline
% \pg@cvloop, \pg@noncvloopline, \check@bnspts, and
% \pg@cvloopline are used only by combined gradetable:
\def\pg@cvloop{%
\addtocounter{@iterator}{1}%
% Add the bonus points:
\set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}%
\add@hlfcntrtohlfcntr{tbl@bonuspoints}{tmp@hlfcntr}%
% Add the regular points:
\set@hlfcntr{tmp@hlfcntr}{\csname pointsonpage@\romannumeral
\csname c@@iterator\endcsname\endcsname}%
\add@hlfcntrtohlfcntr{tbl@points}{tmp@hlfcntr}%
% Print a line of the table, if there are points on this page:
\ifhlfcntr@pos{tmp@hlfcntr}%
\pg@noncvloopline
\else
\check@bnspts
\fi
% Do the tail recursion bit:
\ifnum \the@iterator < \tbl@lastp\relax
\let\next@pg@cvloop=\pg@cvloop
\else
\let\next@pg@cvloop=\relax
\fi
\next@pg@cvloop
}% pg@cvloop
\def\pg@noncvloopline{%
% We still don't understand why we need to hide this inside of a
% macro; there's some weird interaction with the \ifnum checking to
% see if something is ``>0''; checking that something is ``=0''
% doesn't cause the ``\ifnum incomplete'' (or whatever) error.
\pageref{firstpoints@onpage@\arabic{@iterator}}&
\pointsonpage{\the@iterator}&
\bonuspointsonpage{\the@iterator}&\\
\hline
}% pg@noncvloopline
\def\check@bnspts{%
% We still don't understand why we need to hide this inside of a
% macro; there's some weird interaction with the \ifnum checking to
% see if something is ``>0''; checking that something is ``=0''
% doesn't cause the ``\ifnum incomplete'' (or whatever) error.
\set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}%
\ifhlfcntr@pos{tmp@hlfcntr}%
\pg@cvloopline
\fi
}% check@bnspts
\def\pg@cvloopline{%
% We still don't understand why we need to hide this inside of a
% macro; there's some weird interaction with the \ifnum checking to
% see if something is ``>0''; checking that something is ``=0''
% doesn't cause the ``\ifnum incomplete'' (or whatever) error.
\pageref{firstbonuspoints@onpage@\arabic{@iterator}}&
\pointsonpage{\the@iterator}&
\bonuspointsonpage{\the@iterator}&\\
\hline
}% pg@cvloopline
% \pg@ptcvloop, \pg@ptnoncvloopline, \check@ptbnspts, and
% \pg@ptcvloopline are used only by combined pointtable:
\def\pg@ptcvloop{%
\addtocounter{@iterator}{1}%
% Add the bonus points:
\set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}%
\add@hlfcntrtohlfcntr{tbl@bonuspoints}{tmp@hlfcntr}%
% Add the regular points:
\set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}%
\add@hlfcntrtohlfcntr{tbl@points}{tmp@hlfcntr}%
% Print a line of the table, if there are points on this page:
\ifhlfcntr@pos{tmp@hlfcntr}%
\pg@ptnoncvloopline
\else
\check@ptbnspts
\fi
% Do the tail recursion bit:
\ifnum \the@iterator < \tbl@lastp\relax
\let\next@pg@ptcvloop=\pg@ptcvloop
\else
\let\next@pg@ptcvloop=\relax
\fi
\next@pg@ptcvloop
}% pg@ptcvloop
\def\pg@ptnoncvloopline{%
% We still don't understand why we need to hide this inside of a
% macro; there's some weird interaction with the \ifnum checking to
% see if something is ``>0''; checking that something is ``=0''
% doesn't cause the ``\ifnum incomplete'' (or whatever) error.
\pageref{firstpoints@onpage@\arabic{@iterator}}&
\pointsonpage{\the@iterator}&
\bonuspointsonpage{\the@iterator}\\
\hline
}% pg@ptnoncvloopline
\def\check@ptbnspts{%
% We still don't understand why we need to hide this inside of a
% macro; there's some weird interaction with the \ifnum checking to
% see if something is ``>0''; checking that something is ``=0''
% doesn't cause the ``\ifnum incomplete'' (or whatever) error.
\set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}%
\ifhlfcntr@pos{tmp@hlfcntr}%
\pg@ptcvloopline
\fi
}% check@ptbnspts
\def\pg@ptcvloopline{%
% We still don't understand why we need to hide this inside of a
% macro; there's some weird interaction with the \ifnum checking to
% see if something is ``>0''; checking that something is ``=0''
% doesn't cause the ``\ifnum incomplete'' (or whatever) error.
\pageref{firstbonuspoints@onpage@\arabic{@iterator}}&
\pointsonpage{\the@iterator}&
\bonuspointsonpage{\the@iterator}\\
\hline
}% pg@ptcvloopline
% \pg@bvloop and \pg@bvloopline are used only by bonus gradetable:
\def\pg@bvloop{%
\addtocounter{@iterator}{1}%
\set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}%
\ifhlfcntr@pos{tmp@hlfcntr}%
\add@hlfcntrtohlfcntr{tbl@bonuspoints}{tmp@hlfcntr}%
\pg@bvloopline
\fi
\ifnum \the@iterator < \tbl@lastp\relax
\let\next@pg@bvloop=\pg@bvloop
\else
\let\next@pg@bvloop=\relax
\fi
\next@pg@bvloop
}% pg@bvloop
\def\pg@bvloopline{%
% We still don't understand why we need to hide this inside of a
% macro; there's some weird interaction with the \ifnum checking to
% see if something is ``>0''; checking that something is ``=0''
% doesn't cause the ``\ifnum incomplete'' (or whatever) error.
\pageref{firstbonuspoints@onpage@\arabic{@iterator}} &
\bonuspointsonpage{\the@iterator}&\\
\hline
}% pg@bvloopline
% \pg@ptbvloop and \pg@ptbvloopline are used only by bonus pointtable:
\def\pg@ptbvloop{%
\addtocounter{@iterator}{1}%
\set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}%
\ifhlfcntr@pos{tmp@hlfcntr}%
\add@hlfcntrtohlfcntr{tbl@bonuspoints}{tmp@hlfcntr}%
\pg@ptbvloopline
\fi
\ifnum \the@iterator < \tbl@lastp\relax
\let\next@pg@ptbvloop=\pg@ptbvloop
\else
\let\next@pg@ptbvloop=\relax
\fi
\next@pg@ptbvloop
}% pg@ptbvloop
\def\pg@ptbvloopline{%
% We still don't understand why we need to hide this inside of a
% macro; there's some weird interaction with the \ifnum checking to
% see if something is ``>0''; checking that something is ``=0''
% doesn't cause the ``\ifnum incomplete'' (or whatever) error.
\pageref{firstbonuspoints@onpage@\arabic{@iterator}} &
\bonuspointsonpage{\the@iterator}\\
\hline
}% pg@ptbvloopline
%--------------------------------------------------------------------
% Horizontal, indexed by pages:
% For a horizontal table, before we begin we need to know how many
% pages there are with points on them:
\newcounter{num@cols}
\def\count@pgswpts{%
% We're called by \@htblpages.
% \count@pgswpts is used for horizontal
% grade tables and point tables,
% bonus, non-bonus, and combined.
\setcounter{num@cols}{0}%
\setcounter{@iterator}{\tbl@firstp}%
\addtocounter{@iterator}{-1}%
\if@bonus
\docount@pgswbpts
\else
\if@combined
\docount@pgswcpts
\else
\docount@pgswpts
\fi
\fi
}% count@pgswpts
\def\docount@pgswcpts{%
% Called by \count@pgswpts
% \docount@pgswcpts is used for combined gradetable.
% Count the number of pages in range with any kind of point (bonus
% or non-bonus):
\addtocounter{@iterator}{1}%
\set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}%
\ifhlfcntr@pos{tmp@hlfcntr}%
\addtocounter{num@cols}{1}%
\else
\check@bnsptpage
\fi
\ifnum \the@iterator < \tbl@lastp\relax
\let\next@docount=\docount@pgswcpts
\else
\let\next@docount=\relax
\fi
\next@docount
}% docount@pgswcpts
\def\check@bnsptpage{%
% We don't understand why we need to hide this inside of a macro:
\set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}%
\ifhlfcntr@pos{tmp@hlfcntr}%
\addtocounter{num@cols}{1}%
\fi
}% check@bnsptpage
\def\docount@pgswpts{%
% Called by \count@pgswpts
% \docount@pgswpts is used for non-bonus gradetable:
\addtocounter{@iterator}{1}%
\set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}%
\ifhlfcntr@pos{tmp@hlfcntr}%
\addtocounter{num@cols}{1}%
\fi
\ifnum \the@iterator < \tbl@lastp\relax
\let\next@docount=\docount@pgswpts
\else
\let\next@docount=\relax
\fi
\next@docount
}% docount@pgswpts
\def\docount@pgswbpts{%
% Called by \count@pgswpts
% \docount@pgswbpts is used for bonusgradetable:
\addtocounter{@iterator}{1}%
\set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}%
\ifhlfcntr@pos{tmp@hlfcntr}%
\addtocounter{num@cols}{1}%
\fi
\ifnum \the@iterator < \tbl@lastp\relax
\let\next@docount=\docount@pgswbpts
\else
\let\next@docount=\relax
\fi
\next@docount
}% docount@pgswbpts
%--------------------------------------------------------------------
\def\@htblpages{%
% We get here from \@whchtblpgs.
% Horizontal grade or point table, by pages:
\begingroup
\renewcommand\arraystretch{\@gtblstretch}%
% Set num@cols equal to the number of pages with points (of
% correct type):
\count@pgswpts
% Suppress ``extra alignment tab'' errors if \thenum@cols = 0:
\ifnum \thenum@cols = 0\relax
\setcounter{num@cols}{1}%
\fi
\set@hlfcntr{tbl@points}{0}%
\set@hlfcntr{tbl@bonuspoints}{0}%
\if@bonus
% Horizontal bonus grade or point table, by pages:
\begin{tabular}{|l|*{\thenum@cols}{c|}c|}
\hline
{\@bhpgword}& \setcounter{@iterator}{\tbl@firstp}%
\addtocounter{@iterator}{-1}\do@bpgnumloop
{\@bhtword}\\
\hline
{\@bhpword}& \setcounter{@iterator}{\tbl@firstp}%
\addtocounter{@iterator}{-1}\do@bpgptloop
\prt@tablebonuspoints\\
\hline
% If it's a grade table, do the line for scores:
\if@scores
{\@bhsword}& \setcounter{@iterator}{\tbl@firstp}%
\addtocounter{@iterator}{-1}\do@bpgsloop
\\
\hline
\fi
\end{tabular}%
\else
\if@combined
% Horizontal combined grade or point table, by pages:
\begin{tabular}{|l|*{\thenum@cols}{c|}c|}
\hline
{\@chpgword}& \setcounter{@iterator}{\tbl@firstp}%
\addtocounter{@iterator}{-1}\do@cpgnumloop
{\@chtword}\\
\hline
{\@chpword}& \setcounter{@iterator}{\tbl@firstp}%
\addtocounter{@iterator}{-1}\do@cpgptloop
\prt@tablepoints\\
\hline
{\@chbpword}& \setcounter{@iterator}{\tbl@firstp}%
\addtocounter{@iterator}{-1}\do@cbpgptloop
\prt@tablebonuspoints\\
\hline
% If it's a grade table, do the line for scores:
\if@scores
{\@chsword}& \setcounter{@iterator}{\tbl@firstp}%
\addtocounter{@iterator}{-1}\do@cpgsloop
\\
\hline
\fi
\end{tabular}%
\else
% Horizontal non-bonus grade or point table, by pages:
\begin{tabular}{|l|*{\thenum@cols}{c|}c|}
\hline
{\@hpgword}& \setcounter{@iterator}{\tbl@firstp}%
\addtocounter{@iterator}{-1}\do@pgnumloop
{\@htword}\\
\hline
{\@hpword}& \setcounter{@iterator}{\tbl@firstp}%
\addtocounter{@iterator}{-1}\do@pgptloop
\prt@tablepoints\\
\hline
% If it's a grade table, do the line for scores:
\if@scores
{\@hsword}& \setcounter{@iterator}{\tbl@firstp}%
\addtocounter{@iterator}{-1}\do@pgsloop
\\
\hline
\fi
\end{tabular}%
\fi
\fi
\endgroup
}% @htblpages
%--------------------------------------------------------------------
% \do@pgnumloop, \do@pgptloop, and \do@pgsloop
% are only for non-bonus gradetable.
% \pg@line is also for combinedgradetable.
\def\do@pgnumloop{%
\addtocounter{@iterator}{1}%
\set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}%
\ifhlfcntr@pos{tmp@hlfcntr}%
\pg@line
\fi
\ifnum \the@iterator < \tbl@lastp\relax
\let\next@pgnumloop=\do@pgnumloop
\else
\let\next@pgnumloop=\relax
\fi
\next@pgnumloop
}% \do@pgnumloop
\def\pg@line{%
% We still don't know why we need to hide this inside of a macro:
\pageref{firstpoints@onpage@\arabic{@iterator}}&
}% \pg@line
\def\do@pgptloop{%
\addtocounter{@iterator}{1}%
\set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}%
\ifhlfcntr@pos{tmp@hlfcntr}%
\add@hlfcntrtohlfcntr{tbl@points}{tmp@hlfcntr}%
\pgpt@entry
\fi
\ifnum \the@iterator < \tbl@lastp\relax
\let\next@pgptloop=\do@pgptloop
\else
\let\next@pgptloop=\relax
\fi
\next@pgptloop
}% \do@pgptloop
% \pgpt@entry is used by different macros:
\def\pgpt@entry{%
% We still don't know why we need to hide this inside of a macro:
\pointsonpage{\the@iterator}&
}% \pgpt@entry
\def\do@pgsloop{%
\addtocounter{@iterator}{1}%
\set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}%
\ifhlfcntr@pos{tmp@hlfcntr}%
\pg@sentry
\fi
\ifnum \the@iterator < \tbl@lastp\relax
\let\next@pgsloop=\do@pgsloop
\else
\let\next@pgsloop=\relax
\fi
\next@pgsloop
}% \do@pgsloop
% \pg@sentry is called by several different macros,
% to create different tables:
\def\pg@sentry{%
% We still don't know why we need to hide this inside of a macro:
\hbox to \@cellwidth{\hfill}&
}% \pg@sentry
%--------------------------------------------------------------------
% These things are for bonusgradetable:
% \do@bpgnumloop, \do@bpgptloop, \do@bpgsloop,
% and \bpg@sline are only for bonusgradetable.
% \bpg@line is also used for combinedgradetable.
\def\do@bpgnumloop{%
\addtocounter{@iterator}{1}%
\set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}%
\ifhlfcntr@pos{tmp@hlfcntr}%
\bpg@line
\fi
\ifnum \the@iterator < \tbl@lastp\relax
\let\next@bpgnumloop=\do@bpgnumloop
\else
\let\next@bpgnumloop=\relax
\fi
\next@bpgnumloop
}% do@bpgnumloop
\def\bpg@line{%
% We still don't know why we need to hide this inside of a macro:
\pageref{firstbonuspoints@onpage@\arabic{@iterator}}&
}% bpg@line
\def\do@bpgptloop{%
\addtocounter{@iterator}{1}%
\set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}%
\ifhlfcntr@pos{tmp@hlfcntr}%
\add@hlfcntrtohlfcntr{tbl@bonuspoints}{tmp@hlfcntr}%
\bpgpt@line
\fi
\ifnum \the@iterator < \tbl@lastp\relax
\let\next@bpgptloop=\do@bpgptloop
\else
\let\next@bpgptloop=\relax
\fi
\next@bpgptloop
}% do@bpgptloop
% \bpgpt@line is called by several macros:
\def\bpgpt@line{%
% We still don't know why we need to hide this inside of a macro:
\bonuspointsonpage{\the@iterator}&
}% bpgpt@line
\def\do@bpgsloop{%
\addtocounter{@iterator}{1}%
\set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}%
\ifhlfcntr@pos{tmp@hlfcntr}%
\pg@sentry
\fi
\ifnum \the@iterator < \tbl@lastp\relax
\let\next@bpgsloop=\do@bpgsloop
\else
\let\next@bpgsloop=\relax
\fi
\next@bpgsloop
}% do@bpgsloop
%--------------------------------------------------------------------
% These things are for combinedgradetable:
% \do@cpgnumloop, \cpg@line, \cbpg@line, \do@cpgptloop, \bpgpt@line,
% \do@cpgsloop, \bpg@sline, and \do@cbpgptloop are only for
% combinedgradetable:
\def\do@cpgnumloop{%
\addtocounter{@iterator}{1}%
\set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}%
\ifhlfcntr@pos{tmp@hlfcntr}%
\pg@line
\else
\check@bpgpts
\fi
\ifnum \the@iterator < \tbl@lastp\relax
\let\next@cpgnumloop=\do@cpgnumloop
\else
\let\next@cpgnumloop=\relax
\fi
\next@cpgnumloop
}% do@bpgnumloop
\def\check@bpgpts{%
\set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}%
\ifhlfcntr@pos{tmp@hlfcntr}%
\bpg@line
\fi
}% check@bpgpts
\def\do@cpgptloop{%
\addtocounter{@iterator}{1}%
\set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}%
\ifhlfcntr@pos{tmp@hlfcntr}%
\add@hlfcntrtohlfcntr{tbl@points}{tmp@hlfcntr}%
\pgpt@entry
\else
\check@cbpts
\fi
\ifnum \the@iterator < \tbl@lastp\relax
\let\next@cpgptloop=\do@cpgptloop
\else
\let\next@cpgptloop=\relax
\fi
\next@cpgptloop
}% do@cpgptloop
\def\check@cbpts{%
\set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}%
\ifhlfcntr@pos{tmp@hlfcntr}%
\pgpt@entry
\fi
}% check@cbpts
\def\do@cbpgptloop{%
\addtocounter{@iterator}{1}%
\set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}%
\ifhlfcntr@pos{tmp@hlfcntr}%
\add@hlfcntrtohlfcntr{tbl@bonuspoints}{tmp@hlfcntr}%
\bpgpt@line
\else
\check@cnonpts
\fi
\ifnum \the@iterator < \tbl@lastp\relax
\let\next@cbpgptloop=\do@cbpgptloop
\else
\let\next@cbpgptloop=\relax
\fi
\next@cbpgptloop
}% do@cbpgptloop
\def\check@cnonpts{%
\set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}%
\ifhlfcntr@pos{tmp@hlfcntr}%
\bpgpt@line
\fi
}% check@cnonpts
\def\do@cpgsloop{%
\addtocounter{@iterator}{1}%
\set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}%
\ifhlfcntr@pos{tmp@hlfcntr}%
\pg@sentry
\else
\check@bnsline
\fi
\ifnum \the@iterator < \tbl@lastp\relax
\let\next@cpgsloop=\do@cpgsloop
\else
\let\next@cpgsloop=\relax
\fi
\next@cpgsloop
}% do@cpgsloop
\def\check@bnsline{%
\set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}%
\ifhlfcntr@pos{tmp@hlfcntr}%
\pg@sentry
\fi
}% check@bnsline
%--------------------------------------------------------------------
% \pointsinrange and \bonuspointsinrange:
% We say either \@bonusfalse or \@bonustrue, and then we check it only
% in \do@countloop:
\def\pointsinrange#1{%
\@bonusfalse
\def\tbl@range{#1}%
\@ifundefined{exam@numpoints}%
{\mbox{\normalfont\bf ??}}%
{\read@range}%
}% pointsinrange
\def\bonuspointsinrange#1{%
\@bonustrue
\def\tbl@range{#1}%
\@ifundefined{exam@numpoints}%
{\mbox{\normalfont\bf ??}}%
{\read@range}%
}% bonuspointsinrange
\def\bad@range{%
{\mbox{\normalfont\bf ??}}%
\ClassWarning{exam}{%
Grading range `\tbl@range ' not defined.\MessageBreak
\space\space Run LaTeX again.\MessageBreak
}%
}% bad@range
\def\read@range{%
\@ifundefined{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{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]{%
\@ifundefined{range@#1@firstq}%
{\mbox{\normalfont\bf ??}}%
{\csname range@#1@firstq\endcsname}%
}% firstqinrange
\newcommand{\lastqinrange}[1]{%
\@ifundefined{range@#1@lastq}%
{\mbox{\normalfont\bf ??}}%
{\csname range@#1@lastq\endcsname}%
}% lastqinrange
\newcommand{\numqinrange}[1]{%
\@ifundefined{range@#1@firstq}%
{%
\mbox{\normalfont\bf ??}%
}%
{%
\@ifundefined{range@#1@lastq}%
{%
\mbox{\normalfont\bf ??}%
}%
{%
\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}
\def\printquestions{\printquestionstrue}
%\def\noprintquestions{\printquestionsfalse}
% 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]%
{%
\ifprintquestions % act as a solution
\@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
\else %act as a question
\question
\fi
}{%
\ifprintquestions %act as a solution
\ifprintanswers
\end{TheSolution}%
\endgroup
\else
\egroup
\fi
\fi
% else: act as a question
}%
\newenvironment{quest}[1][0pt]%
{%
\ifprintquestions % act as a question
\question
\else % don't show the question
%\par
%\vspace*{-9mm} %
\setbox\z@\vbox\bgroup
\fi
}{%
\ifprintquestions
% don't do anything
\else % hide the group
\egroup
\fi
}%
\newcommand{\questp}[2]{%
\begin{tabular}{R{0.25cm} p{14.75cm}}
(#1) & #2 \\
\end{tabular}
{}
}
\usepackage{array,booktabs,ragged2e}
\newcolumntype{R}[1]{>{\RaggedLeft\arraybackslash}p{#1}}
\newcommand{\questsp}[2]{%
\hspace*{2em}\begin{tabular}{R{1cm} p{13.15cm}}
(#1) & #2 \\
\end{tabular}
{}
}
\newcommand{\questssp}[1]{%
\hspace*{8em}\begin{tabular}{p{10.75cm}}
#1 \\
\end{tabular}
{}
}
\newcommand{\questpnob}[1]{%
\begin{tabular}{R{0.25cm} p{13.15cm}}
& #1 \\
\end{tabular}
{}
}
\newcommand{\questspnob}[1]{%
\hspace*{2em}\begin{tabular}{R{1cm} p{13.15cm}}
& #1 \\
\end{tabular}
{}
}
% 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 \FrameCommand
\else
\if@shadedsolutions
\def\FrameCommand{\colorbox{SolutionColor}}%
\else
% It's \unframedsolutions:
\def\FrameCommand{}%
\fi
\fi
\MakeFramed{\advance\hsize-\width}%
\solutiontitle
\ignorespaces
}%
{%
\unskip
\endMakeFramed
}%
\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.
\newbox\exam@box
\newenvironment{solutionbox}[1]{%
\@insolutiontrue % cancelled by the end of the environment
\@addpointsfalse % cancelled by the end of the environment
\def\solutionbox@size{#1}% saved for end of environment
\@tempdima=\textwidth
\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
\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:
\hbox to \textwidth{%
\noindent
\hskip\@totalleftmargin
\hskip3\fboxsep\hskip\fboxrule
\box\exam@box\hfill
}%
\par\nointerlineskip
\fi
\endgroup % Finish confining the \Solution@Emphasis
\makeemptybox{\solutionbox@size}%
}
%--------------------------------------------------------------------
%--------------------------------------------------------------------
% 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:
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Create framed or shaded regions that can break across pages using
% \begin{framed} ... \end{framed} -- ordinary frame box (box at margin)
% \begin{shaded} ... \end{shaded} -- shaded background (into margin)
% ... leftbar ... -- line on left side
% \begin{MakeFramed}{settings} ... \end{MakeFramed}
% -- generic frame (for new environments)
%
% The "framed" environment puts the text into "\fbox" with the
% settings "\fboxrule=\FrameRule" and "\fboxsep=\FrameSep".
% You can change these lengths (using "\setlength") and you
% can even change the definition of "\FrameCommand" to use
% much fancier boxes.
%
% In fact, the "shaded" environment just redefines "\FrameCommand"
% to use "\colorbox{shadecolor}" (and you have to define the
% color "shadecolor": \newcolor{shadecolor}...).
%
% A page break is allowed, and even encouraged, before the framed
% environment. If you want to attach some text (a box title) to the
% frame, then the text should be inserted by \FrameCommand
%
% 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 "\width" of the frame itself) as well as a `restore'
% command -- \@parboxrestore or \FrameRestore or something similar.
%
% Expert commands:
% \MakeFramed, \endMakeFramed: the "MakeFramed" environment
% \FrameCommand: command to draw the frame around its argument
% \FrameRestore: restore some text settings, but fewer than \@parboxrestore
% \FrameRule: length register; \fboxrule for default "framed".
% \FrameSep: length register; \fboxsep for default "framed".
% \FrameHeightAdjust: macro; height of frame above baseline at top of page
%
% 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{framed}% using default \FrameCommand
{\MakeFramed {\advance\hsize-\width \FrameRestore}}%
{\endMakeFramed}
\newenvironment{shaded}{%
\def\FrameCommand{\colorbox{shadecolor}}%
\MakeFramed {\FrameRestore}}%
{\endMakeFramed}
\newenvironment{leftbar}{%
\def\FrameCommand{\vrule width 3pt \hspace{10pt}}%
\MakeFramed {\advance\hsize-\width \FrameRestore}}%
{\endMakeFramed}
\chardef\FrameRestore=\catcode`\| % for debug
\catcode`\|=\catcode`\% % (debug: insert space after backslash)
\def\MakeFramed#1{\par
% measure added width and height; call result \width and \height
\setbox\z@\vbox{\vskip-1in \hbox{\hskip-1in
\FrameCommand{\hbox{\vrule \@height .7in \@depth.3in \@width 1in}}}%
\vskip\z@skip}%
\def\width{\wd\z@}\def\height{\ht\z@}%
\edef\fb@frw{\the\width}\edef\fb@frh{\the\height}%
% 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\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\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\height
\penalty9999 \vskip -2\baselineskip \vskip-\height
\penalty9999 % updates \pagetotal
|\message{After clearout, \pagetotal=\the\pagetotal, \pagegoal=\the\pagegoal. }%
\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 \width and \height)
\textwidth\hsize \columnwidth\hsize
%psh: added one line:
\linewidth=\hsize
}
\def\endMakeFramed{\par
\kern\z@ \penalty-100 % put depth into height
\egroup
\begingroup \put@frame \endgroup
%psh: Added one line:
\@totalleftmargin=\saved@totalleftmargin
}
% \put@frame takes the contents of \@tempboxa and puts all, or a piece,
% of it on the page with a frame (\FrameCommand). It recurses until
% all of \@tempboxa has been used up. (\@tempboxa must have zero depth.)
\def\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 \fb@adjheight \put@frame
\else % there's appreciable room left on the page
| \message{\string\pagetotal=\the\pagetotal,
| \string\pagegoal=\the\pagegoal,
| \string\pagestretch=\the\pagestretch,
| \string\pageshrink=\the\pageshrink,
| \string\fb@frh=\fb@frh. \space}
| \message{Box of size \the\ht\@tempboxa\space + \fb@frh}%
\begingroup % temporarily set \dimen@ to be...
\advance\dimen@.8\pageshrink % maximum space available on page
\advance\dimen@-\fb@frh\relax % space available for frame's contents
\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 \fb@frh \@plus\pagestretch \@minus.8\pageshrink
\kern137sp\kern-137sp\penalty-30
\unvbox\@tempboxa}%
\edef\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 \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 -\fb@frh
\setbox\tw@\vbox to\dimen@ii \bgroup
% remove simulated frame and page flexibility:
\vskip -\fb@frh \@plus-\pagestretch \@minus-.8\pageshrink
\unvbox\tw@ \unpenalty\unpenalty
\ifdim\lastkern=-137sp % whole box went to next page
| \message{box split at beginning! }%
\egroup \fb@resto@set \eject % (\vskip for frame size was discarded)
\fb@adjheight
\else %
\egroup \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{\FrameCommand{\box\tw@}}% ??? \centerline bad idea
\hbox{\hskip \saved@totalleftmargin\FrameCommand{\box\tw@}}%
\else
| \message{Zero width means likely blank. Don't frame it (guess)}%
\box\tw@
\fi
\hrule \@height\z@
\eject
\fb@adjheight
\put@frame
\fi\fi\fi\fi\fi
\ifvoid\@tempboxa\else
%psh: Changed the command that inserts the box:
% Instead of \centerline, we shift right by \saved@totalleftmargin:
% \centerline{\FrameCommand{\box\@tempboxa}}%
\hbox{\hskip\saved@totalleftmargin\FrameCommand{\box\@tempboxa}}%
\nointerlineskip \null %{\showoutput \showlists}
\penalty-30 \vskip\topsep
\fi}
\def\fb@adjheight{%
\vbox to\FrameHeightAdjust{}% get proper baseline skip from above.
\penalty\@M \nointerlineskip
\vskip-\FrameHeightAdjust
\penalty\@M} % useful for tops of pages
\edef\zero@glue{\the\z@skip}
\catcode`\|=\FrameRestore
% Provide configuration commands:
\providecommand\FrameCommand{\fboxrule=\FrameRule \fboxsep=\FrameSep \fbox}
\@ifundefined{FrameRule}{\newdimen\FrameRule \FrameRule=\fboxrule}{}
\@ifundefined{FrameSep} {\newdimen\FrameSep \FrameSep =3\fboxsep}{}
% Height of frame above first baseline when frame starts a page:
\providecommand\FrameHeightAdjust{6pt}
% \FrameRestore has parts of \@parboxrestore. See how it is used in the
% "settings" argument of \MakeFrame. Previous behavior can be restored by
% using \@parboxrestore there, or redefining:
% \makeatletter \renewcommand\FrameRestore{\@parboxrestore} \makeatother
\def\FrameRestore{%
\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!):
\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
%---------------------------------------------------------------------
%---------------------------------------------------------------------
%---------------------------------------------------------------------
%---------------------------------------------------------------------
%---------------------------------------------------------------------
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment