diff options
author | Karl Berry <karl@freefriends.org> | 2017-11-16 22:14:35 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2017-11-16 22:14:35 +0000 |
commit | 08c9009c085b5fb5cd2151a112662e3c689b67f7 (patch) | |
tree | 80a04ebaddc8ad615d6c2cbcffa758c540359f70 /Master | |
parent | d703ad4829afa66b9973031c99a5dc1e9dd0c2ba (diff) |
exam (16nov17)
git-svn-id: svn://tug.org/texlive/trunk@45832 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master')
-rw-r--r-- | Master/texmf-dist/doc/latex/exam/README | 9 | ||||
-rw-r--r-- | Master/texmf-dist/doc/latex/exam/exam.md5 | 6 | ||||
-rw-r--r-- | Master/texmf-dist/doc/latex/exam/examdoc.pdf | bin | 463915 -> 481243 bytes | |||
-rw-r--r-- | Master/texmf-dist/doc/latex/exam/examdoc.tex | 874 | ||||
-rw-r--r-- | Master/texmf-dist/tex/latex/exam/exam.cls | 3757 |
5 files changed, 3299 insertions, 1347 deletions
diff --git a/Master/texmf-dist/doc/latex/exam/README b/Master/texmf-dist/doc/latex/exam/README index 96ea4c3a73a..fa68345e764 100644 --- a/Master/texmf-dist/doc/latex/exam/README +++ b/Master/texmf-dist/doc/latex/exam/README @@ -1,4 +1,5 @@ -This is version 2.5 of the exam document class, dated May 7, 2015. +This is version 2.6 of the exam document class, dated October 26, +2017. The exam document class, together with its user's guide examdoc.tex, attempts to make it easy for even a LaTeX novice to prepare exams. @@ -11,7 +12,8 @@ subsubparts of subparts, all with optional point values. each question and the total possible points for that question) or by page number (listing each page with points and the total possible points for that page). A grading table can cover the entire exam or -just a part of the exam. +just a part of the exam. Grading tables can have multiple rows or +multiple columns. 3. Create headers and footers that are each specified in three parts: One part to be left justified, one part to be centered, and one part @@ -38,9 +40,8 @@ lined space, or space filled with a grid. e.g., point values, or table headings), so that they can be adapted to personal taste and/or languages other than English. - The user's guide examdoc.tex attempts to explain all of the -possibilities in a readable way, with many specific examples. +possibilities in a readable way, with many examples. -------------------------------------------------------------------- diff --git a/Master/texmf-dist/doc/latex/exam/exam.md5 b/Master/texmf-dist/doc/latex/exam/exam.md5 index e766cadd796..3d20ce21b91 100644 --- a/Master/texmf-dist/doc/latex/exam/exam.md5 +++ b/Master/texmf-dist/doc/latex/exam/exam.md5 @@ -1,3 +1,3 @@ -3d7b2787a32cf31f94fce30dbebe6a6d exam.cls -0b79dabd1e74952a1f70abe1909330c2 examdoc.tex -6c10f29d36dd8e009e854721b391fb50 examdoc.pdf +01d4eea329c471b4295296dff587c23a exam.cls +831e1841882d3e05abdae653595862d6 examdoc.pdf +a9a481f4f36ec3dbf90ffe63f57ce803 examdoc.tex diff --git a/Master/texmf-dist/doc/latex/exam/examdoc.pdf b/Master/texmf-dist/doc/latex/exam/examdoc.pdf Binary files differindex 2193f665a85..37885b7ca40 100644 --- a/Master/texmf-dist/doc/latex/exam/examdoc.pdf +++ b/Master/texmf-dist/doc/latex/exam/examdoc.pdf diff --git a/Master/texmf-dist/doc/latex/exam/examdoc.tex b/Master/texmf-dist/doc/latex/exam/examdoc.tex index ac7f927d21c..cbcfb377a58 100644 --- a/Master/texmf-dist/doc/latex/exam/examdoc.tex +++ b/Master/texmf-dist/doc/latex/exam/examdoc.tex @@ -8,7 +8,8 @@ % The exam documentclass itself is in the file exam.cls. -%%% Copyright (c) 1997, 2000, 2004, 2008, 2011, 2015 Philip S. Hirschhorn +%%% Copyright (c) 1997, 2000, 2004, 2008, 2011, 2015, 2017 +% Philip S. Hirschhorn % % This work may be distributed and/or modified under the % conditions of the LaTeX Project Public License, either version 1.3 @@ -49,9 +50,9 @@ % without generating pdf warnings: \newcommand{\bs}{\texorpdfstring{\char`\\}{}} -\newcommand{\docversion}{2.5} -\newcommand{\docdate}{May 7, 2015} -%\newcommand{\docdate}{\today} +\newcommand{\docversion}{2.6} +\newcommand{\docdate}{November 5, 2017} +%\newcommand{\docdate}{Draft: \today} %-------------------------------------------------------------------- % @@ -128,8 +129,8 @@ Wellesley College\\ Wellesley, MA 02481\\ psh@math.mit.edu\\[\bigskipamount] - Copyright \copyright~1994, 1997, 2000, 2004, 2008, 2011, 2015 Philip - Hirschhorn\\ + Copyright \copyright~1994, 1997, 2000, 2004, 2008, 2011, 2015, 2017\\ + Philip Hirschhorn\\ All rights reserved} \date{\docdate} @@ -509,8 +510,8 @@ number for the \verb"\stretch" value. For example, space allocated by \verb"\vspace{\stretch{1}}".) In addition to leaving blank space, it's also possible to leave lined -space, dotted lined space, or an empty box. For the full story, see -section~\ref{sec:LeaveSpace}. +space, dotted lined space, space printed with a grid, or an empty box. +For the full story, see section~\ref{sec:LeaveSpace}. %-------------------------------------------------------------------- \subsection{Headers and footers} @@ -687,19 +688,24 @@ the command \verb"\printanswers"\indc{printanswers} at the beginning of the exam (see section~\ref{sec:solutions}). The effect of this is that \begin{itemize} -\item the contents of the - environments\indt{solution}\indt{solutionorbox}% - \indt{solutionorlines}\indt{solutionordottedlines} +\item the contents of the environments + % + \index{solution environment@\texttt{solution} environment}% + % + \indt{solutionorbox}% + \indt{solutionorlines}\indt{solutionordottedlines}% + \indt{solutionorgrid}\indt{solutionbox} \begin{center} \begin{tabular}{l} \verb"solution",\\ \verb"solutionorbox",\\ - \verb"solutionorlines", and\\ - \verb"solutionordottedlines" + \verb"solutionorlines",\\ + \verb"solutionordottedlines",\\ + \verb"solutionorgrid", and\\ + \verb"solutionbox" \end{tabular} \end{center} - (see section~\ref{sec:solutions}) will be printed on the next run of - \LaTeX, + (see section~\ref{sec:solutions}) will be printed, \item any choices of a \texttt{choices}\indt{choices}, \texttt{oneparchoices}\indt{oneparchoices}, \texttt{checkboxes}\indt{checkboxes}, or @@ -1072,11 +1078,27 @@ The default is that the point value will be inserted at the beginning of the question (or part, or subpart, or subsubpart) in parentheses, but \begin{itemize} -\item the command \verb"\pointsinmargin"\indc{pointsinmargin} will - cause the point values to be set in the left margin, +\item the command \verb"\pointsinmargin"\indc{pointsinmargin} (or, + equivalently, the command + \verb"\pointsinleftmargin"\indc{pointsinleftmargin}) will cause the + point values to be set in the left margin, \item the command \verb"\pointsinrightmargin"\indc{pointsinrightmargin} will cause the - point values to be set in the right margin, and + point values to be set in the right margin, +\item the command + \verb"\pointstwosided"\indc{pointstwosided} will cause the + point values to be set + \begin{itemize} + \item in the right margin on odd numbered pages and + \item in the left margin on even numbered pages, + \end{itemize} +\item the command + \verb"\pointstwosidedreversed"\indc{pointstwosidedreversed} will + cause the point values to be set + \begin{itemize} + \item in the left margin on odd numbered pages and + \item in the right margin on even numbered pages, and + \end{itemize} \item the commands \verb"\nopointsinmargin"\indc{nopointsinmargin} and \verb"\nopointsinrightmargin"\indc{nopointsinrightmargin} are equivalent, and either of them will revert to the default situation. @@ -1131,8 +1153,9 @@ Describe the effect on the aircraft industry. section~\ref{sec:pointname}.) \bigskip If you give the command -\verb"\pointsinmargin"\indc{pointsinmargin}, then the above input will -produce instead \pointsinmargin +\verb"\pointsinmargin"\indc{pointsinmargin} (or, equivalently, +\verb"\pointsinleftmargin"\indc{pointsinleftmargin}), then the above input +will produce instead \pointsinmargin \begin{questions} \question[20] Why is there air? @@ -1489,8 +1512,9 @@ points.) If you want some text (e.g., `` points'', or ``\%'') automatically inserted along with the point values when using either -\verb"\pointsinmargin" or \verb"\pointsinrightmargin", see the command -\verb"\marginpointname" in section~\ref{sec:pointname}. +\verb"\pointsinmargin", \verb"\pointsinrightmargin", +\verb"\pointstwosided", or \verb"\pointstwosidedreversed", see the +command \verb"\marginpointname" in section~\ref{sec:pointname}. @@ -3611,7 +3635,8 @@ will cause the example in section~\ref{sec:CorrectChoice} to appear as \index{customization!multiple choice questions|)} %-------------------------------------------------------------------- %-------------------------------------------------------------------- -\section{Escaping the indentation: \texttt{\bs uplevel} and \texttt{\bs fullwidth}} +\section{Escaping the indentation: \texttt{\bs uplevel} and + \texttt{\bs fullwidth}} \label{sec:uplevel} \index{indentation|(} @@ -3708,9 +3733,9 @@ You can accomplish exactly the same thing by replacing \end{verbatim} by \begin{verbatim} - \begin{uplevel} + \begin{EnvUplevel} The following two parts should be answered in classical Greek: - \end{uplevel} + \end{EnvUplevel} \end{verbatim} If you want to give instructions for a group of questions, then the @@ -4081,6 +4106,9 @@ which prints an empty box of height \verb"length" whose width equals that of the current line. That is, the left edge is at the current left margin (which depends on whether we're in a question, part, subpart, or subsubpart) and the right edge is at the right margin. +(The box printed by the \verb"\makeemptybox" command can be printed in +color; for that, see section~\ref{sec:colorboxes}.) + \medskip @@ -4157,6 +4185,7 @@ You can also insert an empty box when solutions are not being printed by including an optional argument with the \verb"solutionoremptybox" environment; see section~\ref{sec:SolSpace}. + \indcstop{makeemptybox} \index{empty box|)} \index{space!empty box|)} @@ -4178,6 +4207,24 @@ 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. +By default, the lines are in black. However, if you give the +commands\indc{colorfillwithlines}\index{color package} +\begin{center} + \begin{tabular}{l} + \verb"\usepackage{color}"\\ + \verb"\colorfillwithlines" + \end{tabular} +\end{center} +then the lines will be in color, by default a light gray. That +default color was defined by the +command\indc{definecolor}\indt{FillWithLinesColor} +\begin{center} + \verb"\definecolor{FillWithLinesColor}{gray}{0.8}" +\end{center} +\index{customization!\texttt{\char`\\fillwithlines}} You can change +the color by giving a new \verb"\definecolor" command to redefine the +color \texttt{FillWithLinesColor}. + \medskip For example, if you type @@ -4301,6 +4348,26 @@ command\index{customization!\texttt{\char`\\fillwithdottedlines}} and that value can be changed by giving a new \verb"\setlength" command. +By default, the dotted lines are in black. However, if you give the +commands\indc{colorfillwithdottedlines}\index{color package} +\begin{center} + \begin{tabular}{l} + \verb"\usepackage{color}"\\ + \verb"\colorfillwithdottedlines" + \end{tabular} +\end{center} +then the dotted lines will be in color, by default a light gray. That +default color was defined by the +command\indc{definecolor}\indt{FillWithDottedLinesColor} +\begin{center} + \verb"\definecolor{FillWithDottedLinesColor}{gray}{0.8}" +\end{center} +\index{customization!\texttt{\char`\\fillwithdottedlines}} You can +change the color by giving a new \verb"\definecolor" command to +redefine the color \texttt{FillWithDottedLinesColor}. + +\medskip + For example, if you type \begin{verbatim} \begin{questions} @@ -4412,7 +4479,7 @@ each square has outer side length equal to \verb"\gridsize+\gridlinewidth". By default, the created grids are in black. However, if you give the -commands\indc{colorgrids} +commands\indc{colorgrids}\index{color package} \begin{center} \begin{tabular}{l} \verb"\usepackage{color}"\\ @@ -4514,7 +4581,7 @@ sure you're using either \verb"\pagestyle{headandfoot}" (the default) or \verb"\pagestyle{foot}" (see section~\ref{sec:pagestyle}), and put the following into your preamble (i.e., after the \verb"\documentclass" command and before the \verb"\begin{document}" - command): + command):\index{color package}\indc{gridfromfoot} \begin{verbatim} \usepackage{color} \colorgrids @@ -4736,14 +4803,13 @@ while if \verb"\printanswers" is in effect, you'll get \printanswers \subsection{Solution environments} \label{sec:SolEnv} \index{solutions|(} -\index{solution environment|(} +\index{solution environment@\texttt{solution} environment|(} \index{answers|(} -\index{environment!solution|(} -\index{environment!solutionorbox|(} -\index{environment!solutionorlines|(} -\index{environment!solutionordottedlines|(} -\index{environment!solutionorgrid|(} -\index{solution environment|(} +\index{environment!solution@\texttt{solution}|(} +\index{environment!solutionorbox@\texttt{solutionorbox}|(} +\index{environment!solutionorlines@\texttt{solutionorlines}|(} +\index{environment!solutionordottedlines@\texttt{solutionordottedlines}|(} +\index{environment!solutionorgrid@\texttt{solutionorgrid}|(} \index{solutionorbox environment|(} \index{solutionorlines environment|(} \index{solutionordottedlines environment|(} @@ -4774,7 +4840,7 @@ document (see section~\ref{sec:answers}). The six environments for solutions are divided into two types. \begin{itemize} -\item The \texttt{solutionorbox} environment (see +\item The \texttt{solutionbox} environment (see section~\ref{sec:solutionbox}) always prints a box, of whatever size you choose, and if solutions are being printed it prints the solution inside of the box. @@ -4796,10 +4862,11 @@ The six environments for solutions are divided into two types. differ only in the type of space they leave when you do include the optional argument and solutions are not being printed. - The differences between these five environments are that if the - optional argument is used and solutions are not being printed, - then\index{solution environment!optional argument} + optional argument is used and solutions are not being printed, then + % + \index{solution environment@\texttt{solution} environment!optional argument} + % \begin{itemize} \item the \verb"solution" enviroment inserts that amount of blank space, as if you had given a \verb"\vspace*"\indc{vspace*} command @@ -4831,15 +4898,14 @@ points are not added to the points of the question or the points on the page, and do not affect any gradetables or pointtables. -\index{solution environment|)} \index{solutionorbox environment|)} \index{solutionorlines environment|)} \index{solutionordottedlines environment|)} -\index{environment!solution|)} -\index{environment!solutionorbox|)} -\index{environment!solutionorlines|)} -\index{environment!solutionordottedlines|)} -\index{environment!solutionorgrid|)} +\index{environment!solution@\texttt{solution}|)} +\index{environment!solutionorbox@\texttt{solutionorbox}|)} +\index{environment!solutionorlines@\texttt{solutionorlines}|)} +\index{environment!solutionordottedlines@\texttt{solutionordottedlines}|)} +\index{environment!solutionorgrid@\texttt{solutionorgrid}|)} %-------------------------------------------------------------------- \subsection{The appearance of the solution} @@ -4864,7 +4930,10 @@ environments can be printed either \item with no framing or shading (see section~\ref{sec:SolPlain}). \end{itemize} In all of these cases, solutions can be broken across pages, and the -part on each page will be boxed or shaded as appropriate. +part on each page will be boxed or shaded as appropriate. In +addition, the \verb"\SolutionEmphasis" command can be used to +highlight the text of the solution (using, e.g., boldface, or italics, +or color); see section~\ref{sec:SolEmph}. %-------------------------------------------------------------------- @@ -4874,7 +4943,8 @@ part on each page will be boxed or shaded as appropriate. By default, the solution is printed inside of a box (i.e., an \verb"\fbox"\indc{fbox}), and if the solution is broken across pages, -then each piece is enclosed in a box. There is also a +then each piece is enclosed in a box. (The box can be printed in +color; for that, see section~\ref{sec:colorboxes}.) There is also a \verb"\shadedsolutions"\indc{shadedsolutions} command to instead have the solution printed in a \verb"\colorbox"\indc{colorbox} (i.e., printed on a shaded background); for this, you must load the @@ -5040,6 +5110,56 @@ which returns you to the default situation. \index{solution!no framing or shading|)} %-------------------------------------------------------------------- +\subsection{Imported graphics in solutions} +\label{sec:graphics} +\index{graphics|(} +\indc{includegraphics} + +Graphics can be included inside solution environments using the +\verb"\includegraphics" command, as long as you've given the command +\verb"\usepackage{graphicx}" in the preamble (i.e., after the +\verb"\documentclass" command and before the \verb"\begin{document}" + command). + +A problem sometimes arises because many \LaTeX{} manuals say that you +should put an \verb"\includegraphics" command inside of a +\texttt{figure} environment, so that it will float to a convenient +place on a page. The problem with this is that floating environments +can't appear inside of a solution environment. + +There are (at least) two solutions to this problem, depending on +whether you really want the graphic to float away from the location at +which you've typed it. +\begin{itemize} +\item If you want the graphic to float, put the \texttt{figure} + environment \emph{outside} of the solution environment, using + \verb"\ifprintanswers" to ensure that it's printed only when + solutions are being printed, as in +\begin{verbatim} +\ifprintanswers + \begin{figure} + \includegraphics[width=7cm]{myfigure.pdf} + \caption{This is a lovely figure} + \label{fig:lovely} + \end{figure} +\fi +\end{verbatim} +\item If you \emph{don't} want the graphic to float, then omit the + \texttt{figure} environment and just put the \verb"\includegraphics" + command wherever you want the graphic to appear. If you want the + graphic to have a caption, then put the command + \verb"\usepackage{caption}" in your preamble and then use the + \verb"\captionof" command, as in +\begin{verbatim} +\includegraphics[width=7cm]{myfigure.pdf} +\captionof{figure}{This is a lovely figure} +\label{fig:lovely} +\end{verbatim} +\end{itemize} + +\index{graphics|)} + +%-------------------------------------------------------------------- \subsection{Customizing the solution} \label{sec:SolCust} \index{customization!solution|(} @@ -5162,7 +5282,7 @@ solution is printed. For the default definition of the \label{sec:SolSpace} \index{space!for answers|(} \index{answer space|(} -\index{solution environment!optional argument|(} +\index{solution environment@\texttt{solution} environment!optional argument|(} Each of the \texttt{solution}, \texttt{solutionorbox}, \texttt{solutionorlines}, \texttt{solutionordottedlines}, and @@ -5170,7 +5290,9 @@ Each of the \texttt{solution}, \texttt{solutionorbox}, \begin{itemize} \item In a % - \verb"solution"\indtsub{solution}{optional argument} + \verb"solution" + % + \index{solution environment@\texttt{solution} environment!optional argument} % environment this is an amount of blank space to be left (just as if you had used a \verb"\vspace*"\indc{vspace*} command; see @@ -5313,13 +5435,16 @@ solutions are being printed. \emph{Note:} The document class option \texttt{cancelspace} and the command \verb"\cancelspace" only affect the optional arguments of of -the five solution environments; they do \emph{not} affect any -\verb"\vspace" commands that you put into the file. +the five solution environments \texttt{solution}, +\texttt{solutionorbox}, \texttt{solutionorlines}, +\texttt{solutionordottedlines}, and \texttt{solutionorgrid}; they do +\emph{not} affect any \verb"\vspace" commands that you put into the +file. \indcstop{nocancelspace} \indcstop{cancelspace} -\index{solution environment!optional argument|)} +\index{solution environment@\texttt{solution} environment!optional argument|)} %-------------------------------------------------------------------- \subsection{The solutionbox environment} @@ -5348,7 +5473,11 @@ environments (\texttt{solution}, \texttt{solutionorbox}, \end{enumerate} 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. +printed inside of the box. (The box printed by the +\texttt{solutionbox} environment can be printed in color; for that, +see section~\ref{sec:colorboxes}.) + + For example, if you type \begin{verbatim} @@ -5375,11 +5504,69 @@ being printed, then you'll get If solutions \emph{are} being printed, then the exact same box would be printed, but it would have the solution printed inside of it. - - \index{answer space|)} \index{space!for answers|)} +%-------------------------------------------------------------------- +\subsection{Solution box frames in color} +\label{sec:colorboxes} + +The frames that are printed by the commands that create solution boxes +can be printed in color. (By ``frame'' we mean the four lines that +constitute the box, not any solution that may appear inside that box.) +This includes +\begin{itemize} +\item the box printed by the \texttt{solutionbox} + environment\index{customization!\texttt{solutionbox}} + (see section~\ref{sec:solutionbox}), +\item the box printed by the \verb"\makeemptybox" + command\index{customization!\texttt{\char`\\makeemptybox}} + (see section~\ref{sec:EmptyBox}), +\item the box printed by the optional argument of the + \texttt{solutionorbox} + environment\index{customization!\texttt{solutionorbox}} (see + section~\ref{sec:SolEnv}), and +\item the box printed around the solution by + the\index{customization!\texttt{solution} environments} + \begin{itemize} + \item \texttt{solution}, + \item \texttt{solutionorbox}, + \item \texttt{solutionorlines}, + \item \texttt{solutionordottedlines}, and + \item \texttt{solutionorgrid} + \end{itemize} + environments when solutions are being printed and the default + solution appearance (\verb"\framedsolutions") is being used (see + section~\ref{sec:SolBox}). +\end{itemize} + +If you've given the command +\begin{center} + \verb"\usepackage{color}"\index{color package} +\end{center} +in the preamble of your document (i.e., after the +\verb"\documentclass" command and before the \verb"\begin{document}" +command), then you can give the command\indc{colorsolutionboxes} +\begin{center} + \verb"\colorsolutionboxes" +\end{center} +to have the frame printed in color. The color of the frame will be +\texttt{SolutionBoxColor}, the default value of which was created by +the command\indc{definecolor}\indt{SolutionBoxColor} +\begin{center} + \verb"\definecolor{SolutionBoxColor}{gray}{0.8}" +\end{center} +You can change the color by giving a new \verb"\definecolor" command, +but you must do this \emph{after} the \verb"\colorsolutionboxes" +command. + +To cancel color \texttt{solutionbox} frames and return to black, give +the command\indc{nocolorsolutionboxes} +\begin{center} + \verb"\nocolorsolutionboxes" +\end{center} + + %-------------------------------------------------------------------- \subsection{Changes depending on whether or not solutions are @@ -5473,7 +5660,7 @@ environment will have no effect. \indcstop{ifprintanswers} \index{answers|)} -\index{solution environment|)} +\index{solution environment@\texttt{solution} environment|)} \index{solutions|)} @@ -5721,15 +5908,194 @@ page. If this becomes a problem, then you can either \item create grading ranges (see section~\ref{sec:PartialTable}) and print several partial grading tables, making each one small enough to fit on the page, or -\item use the \verb"\pointsofquestion" command (see - section~\ref{sec:pointsofq}) or the \verb"\pointsonpage" command - (see section~\ref{sec:pointsonp}) to create a custom \verb"tabular" - environment that has more rows (or columns) than the tables produced - by the \verb"gradingtable" command. +\item use the \verb"\multirowgradetable" or + \verb"\multicolumngradetable" command (see section~\ref{sec:MulRows} + and section~\ref{sec:MulCols}) to create a grading table with + multiple rows or multiple columns. \end{itemize} \index{grading table!by question number or page number|)} %-------------------------------------------------------------------- +\subsubsection{Grading tables with multiple rows} +\label{sec:MulRows} + +If you want to print a horizontal grading table but the table would be +too large for the page, you can print a grading table with multiple +rows by giving the command +\begin{center} + \verb"\multirowgradetable{numrows}[questions or pages]" +\end{center} +\indc{multirowgradetable}Note that the first argument +(\texttt{numrows}) is \emph{required}, and so it is enclosed in +braces, but the second argument (\texttt{questions} or \texttt{pages}) +is optional, and is enclosed in square brackets, just as in the +\verb"\gradetable" command (see section~\ref{sec:GradeTables}). For +example, to print a grading table with 3 rows indexed by questions, +you would give the command +\begin{center} + \verb"\multirowgradetable{3}[questions]" +\end{center} +The command +\begin{center} + \verb"\gradetable[h][questions]" +\end{center} +is equivalent to +\begin{center} + \verb"\multirowgradetable{1}[questions]" +\end{center} + +The distance between the rows of a multirow table is +\verb"\doublerulesep", the default value of which is 2.0pt. You can +change that using a \verb"\setlength" command, as in +\begin{center} + \verb"\setlength{\doublerulesep}{0.5in}" +\end{center} +(see section~\ref{sec:CustTable}). + +For example, if an exam has 16 questions, each worth a total of 15 +points, and you type +\begin{verbatim} +\begin{center} + \setlength{\doublerulesep}{0.25in} + \multirowgradetable{2}[questions] +\end{center} +\end{verbatim} +then you'll get +\begin{center} + \setlength{\doublerulesep}{0.25in} + \cwidth=2em + \renewcommand{\arraystretch}{1.5} + \begin{tabular}{|l|c|c|c|c|c|c|c|c|c|} + \hline + Question:& 1& 2& 3& 4& 5& 6& 7& 8& 9\\ + \hline + Points:& + 15& + 15& + 15& + 15& + 15& + 15& + 15& + 15& + 15\\ + \hline + Score:& + \hbox to \cwidth{\hfill}& + \hbox to \cwidth{\hfill}& + \hbox to \cwidth{\hfill}& + \hbox to \cwidth{\hfill}& + \hbox to \cwidth{\hfill}& + \hbox to \cwidth{\hfill}& + \hbox to \cwidth{\hfill}& + \hbox to \cwidth{\hfill}& + \\ + \hline + \hline + Question:& 10& 11& 12& 13& 14& 15& 16&& Total\\ + \hline + Points:& + 15& + 15& + 15& + 15& + 15& + 15& + 15& + & + 240\\ + \hline + Score:& + \hbox to \cwidth{\hfill}& + \hbox to \cwidth{\hfill}& + \hbox to \cwidth{\hfill}& + \hbox to \cwidth{\hfill}& + \hbox to \cwidth{\hfill}& + \hbox to \cwidth{\hfill}& + \hbox to \cwidth{\hfill}& + \hbox to \cwidth{\hfill}& + \\ + \hline + \end{tabular} +\end{center} + + +%-------------------------------------------------------------------- +\subsubsection{Grading tables with multiple columns} +\label{sec:MulCols} + +If you want to print a vertical grading table but the table would be +too large for the page, you can print a grading table with multiple +columns by giving the command +\begin{center} + \verb"\multicolumngradetable{numcols}[questions or pages]" +\end{center} +\indc{multicolumngradetable}Note that the first argument +(\texttt{numcols}) is \emph{required}, and so it is enclosed in +braces, but the second argument (\texttt{questions} or \texttt{pages}) +is optional, and is enclosed in square brackets, just as in the +\verb"\gradetable" command (see section~\ref{sec:GradeTables}). For +example, to print a grading table with 3 columns indexed by questions, +you would give the command +\begin{center} + \verb"\multicolumngradetable{3}[questions]" +\end{center} +The command +\begin{center} + \verb"\gradetable[v][questions]" +\end{center} +is equivalent to +\begin{center} + \verb"\multicolumngradetable{1}[questions]" +\end{center} + +The distance between the columns of a multicolumn table is +\verb"\doublerulesep", the default value of which is 2.0pt. You can +change that using a \verb"\setlength" command, as in +\begin{center} + \verb"\setlength{\doublerulesep}{0.5in}" +\end{center} +(see section~\ref{sec:CustTable}). + +For example, if an exam has 10 questions, each worth a total of 15 +points, and you type +\begin{verbatim} +\begin{center} + \setlength{\doublerulesep}{0.5in} + \multicolumngradetable{2}[questions] +\end{center} +\end{verbatim} +then you'll get +\begin{center} + \cwidth=2em + \setlength{\doublerulesep}{0.5in} + \renewcommand{\arraystretch}{1.5} + \begin{tabular}{|c|c|c|c|c|c|c|} + \cline{1-3}\cline{5-7} + Question& Points& Score& + \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}& + Question& Points& Score\\ + \cline{1-3}\cline{5-7} + 1& 15& \hbox to \cwidth{\hfill}&&7& 15& \hbox to \cwidth{\hfill}\\ + \cline{1-3}\cline{5-7} + 2& 15& \hbox to \cwidth{\hfill}&&8& 15& \hbox to \cwidth{\hfill}\\ + \cline{1-3}\cline{5-7} + 3& 15& \hbox to \cwidth{\hfill}&&9& 15& \hbox to \cwidth{\hfill}\\ + \cline{1-3}\cline{5-7} + 4& 15& \hbox to \cwidth{\hfill}&&10& 15& \hbox to \cwidth{\hfill}\\ + \cline{1-3}\cline{5-7} + 5& 15& \hbox to \cwidth{\hfill}&& & & \hbox to \cwidth{\hfill}\\ + \cline{1-3}\cline{5-7} + 6& 15& \hbox to \cwidth{\hfill}&&Total:& 150& \hbox to \cwidth{\hfill}\\ + \cline{1-3}\cline{5-7} + + \cline{1-3}\cline{5-7} + \end{tabular} +\end{center} + + + +%-------------------------------------------------------------------- \subsection{Point tables indexed by question number or by page number} \label{sec:PointTables} @@ -5893,15 +6259,88 @@ page. If this becomes a problem, then you can either \item create grading ranges (see section~\ref{sec:PartialTable}) and print several partial point tables, making each one small enough to fit on the page, or -\item use the \verb"\pointsofquestion" command (see - section~\ref{sec:pointsofq}) or the \verb"\pointsonpage" command - (see section~\ref{sec:pointsonp}) to create a custom \verb"tabular" - environment that has more rows (or columns) than the tables produced - by the \verb"pointtable" command. +\item use the \verb"\multirowpointtable" or + \verb"\multicolumnpointtable" command (see + section~\ref{sec:PtMulRows} and section~\ref{sec:PtMulCols}) to + create a point table with multiple rows or multiple columns. \end{itemize} \index{point table!by question number or page number|)} %-------------------------------------------------------------------- +\subsubsection{Point tables with multiple rows} +\label{sec:PtMulRows} + +If you want to print a horizontal point table but the table would be +too large for the page, you can print a point table with multiple +rows by giving the command +\begin{center} + \verb"\multirowpointtable{numrows}[questions or pages]" +\end{center} +\indc{multirowpointtable}Note that the first argument +(\texttt{numrows}) is \emph{required}, and so it is enclosed in +braces, but the second argument (\texttt{questions} or \texttt{pages}) +is optional, and is enclosed in square brackets, just as in the +\verb"\pointtable" command (see section~\ref{sec:PointTables}). For +example, to print a point table with 3 rows indexed by questions, you +would give the command +\begin{center} + \verb"\multirowpointtable{3}[questions]" +\end{center} +The command +\begin{center} + \verb"\pointtable[h][questions]" +\end{center} +is equivalent to +\begin{center} + \verb"\multirowpointtable{1}[questions]" +\end{center} + +The distance between the rows of a multirow table is +\verb"\doublerulesep", the default value of which is 2.0pt. You can +change that using a \verb"\setlength" command, as in +\begin{center} + \verb"\setlength{\doublerulesep}{0.5in}" +\end{center} +(see section~\ref{sec:CustTable}). + +%-------------------------------------------------------------------- +\subsubsection{Point tables with multiple columns} +\label{sec:PtMulCols} + +If you want to print a vertical point table but the table would be +too large for the page, you can print a point table with multiple +columns by giving the command +\begin{center} + \verb"\multicolumnpointtable{numcols}[questions or pages]" +\end{center} +\indc{multicolumnpointtable}Note that the first argument +(\texttt{numcols}) is \emph{required}, and so it is enclosed in +braces, but the second argument (\texttt{questions} or \texttt{pages}) +is optional, and is enclosed in square brackets, just as in the +\verb"\pointtable" command (see section~\ref{sec:PointTables}). For +example, to print a point table with 3 columns indexed by questions, +you would give the command +\begin{center} + \verb"\multicolumnpointtable{3}[questions]" +\end{center} +The command +\begin{center} + \verb"\pointtable[v][questions]" +\end{center} +is equivalent to +\begin{center} + \verb"\multicolumnpointtable{1}[questions]" +\end{center} + +The distance between the columns of a multicolumn table is +\verb"\doublerulesep", the default value of which is 2.0pt. You can +change that using a \verb"\setlength" command, as in +\begin{center} + \verb"\setlength{\doublerulesep}{0.5in}" +\end{center} +(see section~\ref{sec:CustTable}). + +%-------------------------------------------------------------------- \subsection{Table entries as clickable links} \label{sec:Clickable} \index{grading table!clickable links} @@ -5932,7 +6371,8 @@ instead of the numbers of those questions, and those question titles will be clickable links that take you to the corresponding question. %-------------------------------------------------------------------- -\subsection{Grading ranges, partial grading tables, and partial point tables} +\subsection{Grading ranges, partial grading tables, and partial point + tables} \label{sec:PartialTable} \index{grading range|(} \indcstart{begingradingrange} @@ -6006,6 +6446,91 @@ section~\ref{sec:BonusTables} and section~\ref{sec:CombinedTables}). \indcstop{begingradingrange} \index{grading range|)} + +%-------------------------------------------------------------------- +\subsubsection{Partial grading tables and partial point tables with + multiple rows} +\label{sec:PartMulRows} + +If you want to print a horizontal partial grading table or partial +point table but the table would be too large for the page, you can +print a partial grading table or partial point table with multiple +rows by giving the one of the commands +\begin{center} + \verb"\multirowpartialgradetable{numrows}{rangename}[questions or pages]" + \verb"\multirowpartialpointtable{numrows}{rangename}[questions or pages]" +\end{center} +\indc{multirowpartialgradetable}\indc{multirowpartialpointtable}Note +that the first two arguments (\texttt{numrows} and \texttt{rangename}) +are \emph{required}, and so they are enclosed in braces, but the third +argument (\texttt{questions} or \texttt{pages}) is optional, and is +enclosed in square brackets, just as in the \verb"\partialgradetable" +and \verb"\partialpointtable" commands (see +section~\ref{sec:PartialTable}). For example, to print a partial +grading table with 3 rows covering the range \texttt{myrange} indexed +by questions, you would give the command +\begin{center} + \verb"\multirowpartialgradetable{3}{myrange}[questions]" +\end{center} +The command +\begin{center} + \verb"\partialgradetable{myrange}[h][questions]" +\end{center} +is equivalent to +\begin{center} + \verb"\multirowpartialgradetable{1}{myrange}[questions]" +\end{center} + +The distance between the rows of a multirow table is +\verb"\doublerulesep", the default value of which is 2.0pt. You can +change that using a \verb"\setlength" command, as in +\begin{center} + \verb"\setlength{\doublerulesep}{0.5in}" +\end{center} +(see section~\ref{sec:CustTable}). + +%-------------------------------------------------------------------- +\subsubsection{Partial grading tables and partial point tables with + multiple columns} +\label{sec:PartMulCols} + +If you want to print a vertical partial grading table or partial point +table but the table would be too large for the page, you can print a +partial grading table or partial point table with multiple columns by +giving one of the commands +\begin{center} +\verb"\multicolumnpartialgradetable{numcols}{rangename}[questions or pages]"\\ +\verb"\multicolumnpartialpointtable{numcols}{rangename}[questions or pages]" +\end{center} +\indc{multicolumnpartialgradetable}\indc{multicolumnpartialpointtable}Note +that the first two arguments (\texttt{numcols} and +\texttt{rangename}) are \emph{required}, and so they are enclosed in +braces, but the third argument (\texttt{questions} or \texttt{pages}) +is optional, and is enclosed in square brackets, just as in the +\verb"\partialgradetable" and \verb"\partialpointtable" commands (see +section~\ref{sec:PartialTable}). For example, to print a partial +grading table with 3 columns covering the range \texttt{myrange} +indexed by questions, you would give the command +\begin{center} + \verb"\multicolumnpartialgradetable{3}{myrange}[questions]" +\end{center} +The command +\begin{center} + \verb"\partialgradetable{myrange}[v][questions]" +\end{center} +is equivalent to +\begin{center} + \verb"\multicolumnpartialgradetable{1}{myrange}[questions]" +\end{center} + +The distance between the columns of a multicolumn table is +\verb"\doublerulesep", the default value of which is 2.0pt. You can +change that using a \verb"\setlength" command, as in +\begin{center} + \verb"\setlength{\doublerulesep}{0.5in}" +\end{center} +(see section~\ref{sec:CustTable}). + %-------------------------------------------------------------------- \subsection{\texttt{\bs pointsofquestion}} \label{sec:pointsofq} @@ -6114,29 +6639,59 @@ commands that count only the bonus points and ignore the non-bonus points. (There are also commands that create tables listing both non-bonus and bonus points; see section~\ref{sec:CombinedTables}.) These commands, and the non-bonus commands to which they correspond, -are as follows:\indc{bonusgradetable}\indc{bonuspointtable}% -\indc{bonuspointsinrange}\indc{partialbonusgradetable}% -\indc{partialbonuspointtable}\indc{bonuspointsofquestion}% -\indc{bonuspointsonpage} +are as follows: \begin{center} \begin{tabular}{l@{\qquad\qquad}l} Bonus points& Non-bonus points\\[\bigskipamount] \verb"\bonusgradetable"& \verb"\gradetable"\\ - \verb"\bonuspointtable"& \verb"\pointtable"\\[\medskipamount] - \verb"\bonuspointsinrange"& \verb"\pointsinrange"\\ + \verb"\multirowbonusgradetable"& \verb"\multirowgradetable"\\ + \verb"\multicolumnbonusgradetable"& \verb"\multicolumngradetable"\\ + \verb"\bonuspointtable"& \verb"\pointtable"\\ + \verb"\multirowbonuspointtable"& \verb"\multirowpointtable"\\ + \verb"\multicolumnbonuspointtable"& + \verb"\multicolumnpointtable"\\[\medskipamount] \verb"\partialbonusgradetable"& \verb"\partialgradetable"\\ + \verb"\multirowpartialbonusgradetable"& + \verb"\multirowpartialgradetable"\\ + \verb"\multicolumnpartialbonusgradetable"& + \verb"\multicolumnpartialgradetable"\\ \verb"\partialbonuspointtable"& - \verb"\partialpointtable"\\[\medskipamount] + \verb"\partialpointtable"\\ + \verb"\multirowpartialbonuspointtable"& + \verb"\multirowpartialpointtable"\\ + \verb"\multicolumnpartialbonuspointtable"& + \verb"\multicolumnpartialpointtable"\\[\medskipamount] + \verb"\bonuspointsinrange"& \verb"\pointsinrange"\\ \verb"\bonuspointsofquestion"& \verb"\pointsofquestion"\\ \verb"\bonuspointsonpage"& \verb"\pointsonpage" \end{tabular} \end{center} +\indc{bonusgradetable}\indc{bonuspointtable}% +\indc{bonuspointsinrange}\indc{partialbonusgradetable}% +\indc{partialbonuspointtable}\indc{bonuspointsofquestion}% +\indc{bonuspointsonpage}% +\indc{multirowbonusgradetable}% +\indc{multicolumnbonusgradetable}% +\indc{multirowbonuspointtable}% +\indc{multicolumnbonuspointtable}% +\indc{multirowpartialbonusgradetable}% +\indc{multicolumnpartialbonusgradetable}% +\indc{multirowpartialbonuspointtable}% +\indc{multicolumnpartialbonuspointtable}% All of the the commands in the first column count bonus points and ignore non-bonus points, while all of the commands in the second -column count non-bonus points and ignore bonus points. The -\verb"\partialbonusgradetable" and \verb"\partialbonuspointtable" -commands use the same -\verb"\begingradingrange"\indc{begingradingrange} and +column count non-bonus points and ignore bonus points. The commands +\begin{center} + \begin{tabular}{l} + \verb"\partialbonusgradetable",\\ + \verb"\multirowpartialbonusgradetable",\\ + \verb"\multicolumnpartialbonusgradetable",\\ + \verb"\partialbonuspointtable",\\ + \verb"\multirowpartialbonuspointtable", and\\ + \verb"\multicolumnpartialbonuspointtable" + \end{tabular} +\end{center} +use the same \verb"\begingradingrange"\indc{begingradingrange} and \verb"\endgradingrange"\indc{endgradingrange} commands that are used by the corresponding non-bonus versions (see section~\ref{sec:PartialTable}). Just as is described in @@ -6214,14 +6769,15 @@ then you'll get There are commands for producing \emph{combined} grading and point tables, both partial and full, that tabulate both regular points and -bonus points (see section~\ref{sec:Bonus}). These commands, and the +bonus points (see section~\ref{sec:Bonus}). The single-row and +single-column versions of these commands, and the non-bonus and bonus commands to which they correspond, are \indc{combinedgradetable}\indc{combinedpointtable}% \indc{partialcombinedgradetable}\indc{partialcombinedpointtable}% \begin{center} \begin{tabular}{l@{\enskip}l@{\enskip}l} - Non-Bonus points only& Bonus points only& - Combined\\[\medskipamount] + Non-Bonus points only& \multicolumn{1}{c}{Bonus points only}& + \multicolumn{1}{c}{Combined}\\[\medskipamount] \verb"\gradetable"& \verb"\bonusgradetable"& \verb"\combinedgradetable"\\ \verb"\pointtable"& \verb"\bonuspointtable"& @@ -6242,6 +6798,37 @@ and then the same two optional arguments as the other commands (the first being either \verb"[v]" or \verb"[h]" and the second being either \verb"[questions]" or \verb"[pages]"). +The multirow and multicolumn versions of combined grading tables and +combined point tables are produced by the commands +\indc{multirowcombinedgradetable}% +\indc{multirowcombinedpointtable}% +\indc{multirowpartialcombinedgradetable}% +\indc{multirowpartialcombinedpointtable}% +\indc{multicolumncombinedgradetable}% +\indc{multicolumncombinedpointtable}% +\indc{multicolumnpartialcombinedgradetable}% +\indc{multicolumnpartialcombinedpointtable}% +\begin{verbatim} +\multirowcombinedgradetable{numrows}[questions or pages] +\multirowcombinedpointtable{numrows}[questions or pages] +\multirowpartialcombinedgradetable{numrows}{rangename}[questions or pages] +\multirowpartialcombinedpointtable{numrows}{rangename}[questions or pages] +\multicolumncombinedgradetable{numcols}[questions or pages] +\multicolumncombinedpointtable{numcols}[questions or pages] +\multicolumnpartialcombinedgradetable{numcols}{rangename}[questions or pages] +\multicolumnpartialcombinedpointtable{numcols}{rangename}[questions or pages] +\end{verbatim} +The distance between the rows of a multirow table and between the +columns of a multicolumn table is \verb"\doublerulesep", the default +value of which is 2.0pt. You can change that using a +\verb"\setlength" command, as in +\begin{center} + \verb"\setlength{\doublerulesep}{0.5in}" +\end{center} +(see section~\ref{sec:CustTable}). + + + Just as is described in section~\ref{sec:Clickable} for their regular points and section~\ref{sec:BonusTables} for bonus points, if you put the @@ -6412,7 +6999,7 @@ and no other tables will be affected by that \index{grade table!customization|(} \index{point table!customization|(} -There are three ways in which you can customize the appearance of +There are four ways in which you can customize the appearance of grading tables and point tables: \begin{itemize} \item You can change the words (and the fonts) that appear in the @@ -6421,20 +7008,30 @@ grading tables and point tables: you will write the scores. \item You can change the value of \verb"\baselinestretch"\indc{baselinestretch} used for the table. +\item You can change the distance between the rows in a multirow table + and the distance between the columns in a multicolumn table. \end{itemize} For all grading and point tables:\indc{gradetablestretch}\indc{cellwidth} +\indc{doublerulesep}% \begin{center} - \begin{tabular}{l@{\qquad}l} + \begin{tabular}{l@{\qquad}p{3in}} \verb"\cellwidth{length}"& sets the minimum width of the blank cells to - \verb"length"\\ + \verb"length"\\[\medskipamount] \verb"\gradetablestretch{number}"& uses \verb"number" as the - \verb"\baselinestretch" + \verb"\baselinestretch"\\[\medskipamount] + \verb"\setlength{\doublerulesep}{length}"& sets the distance + between the columns of a multicolumn table and the distance + between the rows of a multirow table to \verb"length". \end{tabular} \end{center} -For vertical non-bonus tables (i.e., tables created by -\verb"\gradetable[v]", \verb"\pointtable[v]", -\verb"\partialgradetable{rangename}[v]", or -\verb"\partialpointtable{rangename}[v]"):% +For vertical non-bonus tables: +% (i.e., tables created by +% \verb"\gradetable[v]", \verb"\multicolumngradetable", +% \verb"\pointtable[v]", \verb"\multicolumnpointtable", +% \verb"\partialgradetable{rangename}[v]", +% \verb"\multicolumnpartialgradetable", +% \verb"\partialpointtable{rangename}[v]", or +% \verb"\multicolumnpartialpointtable"):% \indc{vqword}\indc{vpgword}\indc{vpword}\indc{vsword}\indc{vtword} \begin{center} \begin{tabular}{l@{\qquad}l} @@ -6446,10 +7043,11 @@ For vertical non-bonus tables (i.e., tables created by \verb"\vtword{text}"& substitutes \verb"text" for ``Total:'' \end{tabular} \end{center} -For horizontal non-bonus tables (i.e., tables created by -\verb"\gradetable[h]", \verb"\pointtable[h]", -\verb"\partialgradetable{rangename}[h]", or -\verb"\partialpointtable{rangename}[h]"):% +For horizontal non-bonus tables: +% (i.e., tables created by +% \verb"\gradetable[h]", \verb"\pointtable[h]", +% \verb"\partialgradetable{rangename}[h]", or +% \verb"\partialpointtable{rangename}[h]"):% \indc{hqword}\indc{hpgword}\indc{hpword}\indc{hsword}\indc{htword} \begin{center} \begin{tabular}{l@{\qquad}l} @@ -6461,11 +7059,13 @@ For horizontal non-bonus tables (i.e., tables created by \verb"\htword{text}"& substitutes \verb"text" for ``Total'' \end{tabular} \end{center} -For vertical bonus tables (i.e., tables created by -\verb"\bonusgradetable[v]", \verb"\bonuspointtable[v]", -\verb"\partialbonusgradetable{rangename}[v]", or -\verb"\partialbonuspointtable{rangename}[v]"):\indc{bvqword}% -\indc{bvpgword}\indc{bvpword}\indc{bvsword}\indc{bvtword} +For vertical bonus tables: +% (i.e., tables created by +% \verb"\bonusgradetable[v]", \verb"\bonuspointtable[v]", +% \verb"\partialbonusgradetable{rangename}[v]", or +% \verb"\partialbonuspointtable{rangename}[v]"): +\indc{bvqword}\indc{bvpgword}\indc{bvpword}\indc{bvsword}% +\indc{bvtword} \begin{center} \begin{tabular}{l@{\qquad}l} Command& \multicolumn{1}{c}{Effect}\\[\medskipamount] @@ -6476,11 +7076,13 @@ For vertical bonus tables (i.e., tables created by \verb"\bvtword{text}"& substitutes \verb"text" for ``Total:'' \end{tabular} \end{center} -For horizontal bonus tables (i.e., tables created by -\verb"\bonusgradetable[h]", \\ -\verb"\bonuspointtable[h]", -\verb"\partialbonusgradetable{rangename}[h]", or\\ -\verb"\partialbonuspointtable{rangename}[h]"):\indc{bhqword}% +For horizontal bonus tables: +% (i.e., tables created by +% \verb"\bonusgradetable[h]", \\ +% \verb"\bonuspointtable[h]", +% \verb"\partialbonusgradetable{rangename}[h]", or\\ +% \verb"\partialbonuspointtable{rangename}[h]"): +\indc{bhqword}% \indc{bhpgword}\indc{bhpword}\indc{bhsword}\indc{bhtword} \begin{center} \begin{tabular}{l@{\qquad}l} @@ -6492,11 +7094,13 @@ For horizontal bonus tables (i.e., tables created by \verb"\bhtword{text}"& substitutes \verb"text" for ``Total'' \end{tabular} \end{center} -For vertical combined tables (i.e., tables created by -\verb"\combinedgradetable[v]",\\ -\verb"\combinedpointtable[v]", -\verb"\partialcombinedgradetable{rangename}[v]", or\\ -\verb"\partialcombinedpointtable{rangename}[v]"):\indc{cvqword}% +For vertical combined tables: +% (i.e., tables created by +% \verb"\combinedgradetable[v]",\\ +% \verb"\combinedpointtable[v]", +% \verb"\partialcombinedgradetable{rangename}[v]", or\\ +% \verb"\partialcombinedpointtable{rangename}[v]"): +\indc{cvqword}% \indc{cvpgword}\indc{cvpword}\indc{cvbpword}\indc{cvsword}% \indc{cvtword} \begin{center} @@ -6510,11 +7114,13 @@ For vertical combined tables (i.e., tables created by \verb"\cvtword{text}"& substitutes \verb"text" for ``Total:'' \end{tabular} \end{center} -For horizontal combined tables (i.e., tables created by -\verb"\combinedgradetable[h]", \\ -\verb"\combinedpointtable[h]", -\verb"\partialcombinedgradetable{rangename}[h]", or\\ -\verb"\partialcombinedpointtable{rangename}[h]"):\indc{chqword}% +For horizontal combined tables: +% (i.e., tables created by +% \verb"\combinedgradetable[h]", \\ +% \verb"\combinedpointtable[h]", +% \verb"\partialcombinedgradetable{rangename}[h]", or\\ +% \verb"\partialcombinedpointtable{rangename}[h]"): +\indc{chqword}% \indc{chpgword}\indc{chpword}\indc{chbpword}\indc{chsword}% \indc{chtword} \begin{center} @@ -6529,16 +7135,15 @@ For horizontal combined tables (i.e., tables created by \end{tabular} \end{center} If you don't use any of these commands then you get the default -values, which are defined by the commands in the following table: +values, which are defined by the commands in the following +table:\indc{doublerulesep}\indc{gradetablestretch}\indc{cellwidth} \begin{center} - \begin{tabular}{l@{\quad}l@{\quad}l} - \multicolumn{3}{c}{\bfseries Defaults for grading and point + \begin{tabular}{l@{\quad}l} + \multicolumn{2}{c}{\bfseries Defaults for grading and point tables}\\*[\bigskipamount] - \verb"\hqword{Question:}"& \verb"\vqword{Question}"& - \verb"\cellwidth{2em}"\\ - \verb"\hpgword{Page:}"& \verb"\vpgword{Page}"& - \verb"\gradetablestretch{1.5}"\\ - \verb"\hpword{Points:}"& \verb"\vpword{Points}"\\ + \verb"\hqword{Question:}"& \verb"\vqword{Question}"\\ + \verb"\hpgword{Page:}"& \verb"\vpgword{Page}"\\ + \verb"\hpword{Points:}"& \verb"\vpword{Points}"\\ \verb"\hsword{Score:}"& \verb"\vsword{Score}"\\ \verb"\htword{Total}"& \verb"\vtword{Total:}"\\[\medskipamount] % @@ -6553,7 +7158,10 @@ values, which are defined by the commands in the following table: \verb"\chpword{Points:}"& \verb"\cvpword{Points}"\\ \verb"\chbpword{Bonus Points:}"& \verb"\cvbpword{Bonus Points}"\\ \verb"\chsword{Score:}"& \verb"\cvsword{Score}"\\ - \verb"\chtword{Total}"& \verb"\cvtword{Total:}" + \verb"\chtword{Total}"& \verb"\cvtword{Total:}"\\[\medskipamount] + \verb"\cellwidth{2em}"\\ + \verb"\gradetablestretch{1.5}"\\ + \verb"\setlength{\doublerulesep}{2.0pt}" \end{tabular} \end{center} @@ -6888,7 +7496,8 @@ appear on every page, you would give the command \indcstop{runningheader} %-------------------------------------------------------------------- -\subsubsection{Using \texttt{\bs lhead}, \texttt{\bs chead} and \texttt{\bs rhead}} +\subsubsection{Using \texttt{\bs lhead}, \texttt{\bs chead} and + \texttt{\bs rhead}} \label{sec:lhead} \indcstart{lhead} \indcstart{chead} @@ -7022,7 +7631,8 @@ section~\ref{sec:numpages}.) \indcstop{runningfooter} %-------------------------------------------------------------------- -\subsubsection{Using \texttt{\bs lfoot}, \texttt{\bs cfoot}, and \texttt{\bs rfoot}} +\subsubsection{Using \texttt{\bs lfoot}, \texttt{\bs cfoot}, and + \texttt{\bs rfoot}} \label{sec:lfoot} \indcstart{lfoot} \indcstart{cfoot} @@ -7102,15 +7712,6 @@ command\indcsub{extraheadheight}{different on first page} \end{center} gives a half inch of extra head height on the first page and a quarter inch of extra head height on all pages after the first. -If you say -\begin{center} - \verb"\extraheadheight[.5in]{}" -\end{center} -then this will be interpreted as if it was -\begin{center} - \verb"\extraheadheight[.5in]{0in}" -\end{center} -Note that the braces \emph{must} appear. If you give an \verb"\extraheadheight"\indc{extraheadheight} command, it should be in the preamble, i.e., after the \verb"\documentclass" @@ -7151,15 +7752,6 @@ command\indcsub{extrafootheight}{different on first page} \end{center} gives a half inch of extra foot height on the first page and a quarter inch of extra foot height on all pages after the first. -If you say -\begin{center} - \verb"\extrafootheight[.5in]{}" -\end{center} -then this will be interpreted as if it was -\begin{center} - \verb"\extrafootheight[.5in]{0in}" -\end{center} -Note that the braces \emph{must} appear. If you give an \verb"\extrafootheight"\indc{extrafootheight} command, it should be in the preamble, i.e., after the \verb"\documentclass" diff --git a/Master/texmf-dist/tex/latex/exam/exam.cls b/Master/texmf-dist/tex/latex/exam/exam.cls index 5ea596ca0a3..52b4e9d4f45 100644 --- a/Master/texmf-dist/tex/latex/exam/exam.cls +++ b/Master/texmf-dist/tex/latex/exam/exam.cls @@ -3,7 +3,8 @@ % A LaTeX2e document class for preparing exams. %% exam.cls -%% Copyright (c) 1994, 1997, 2000, 2004, 2008, 2011, 2015 Philip S. Hirschhorn +%% Copyright (c) 1994, 1997, 2000, 2004, 2008, 2011, +%% 2015, 2017 Philip S. Hirschhorn % % This work may be distributed and/or modified under the % conditions of the LaTeX Project Public License, either version 1.3 @@ -31,8 +32,8 @@ % from my web page: http://www-math.mit.edu/~psh/ -\def\fileversion{2.5} -\def\filedate{2015/05/07} +\def\fileversion{2.6} +\def\filedate{2017/10/26} %--------------------------------------------------------------------- %--------------------------------------------------------------------- % @@ -85,6 +86,263 @@ % Changelog since version 2.4: %-------------------------------------------------------------------- +% Version 2.6, 2017/09/19 + +% No longer betatest. + +%-------------------------------------------------------------------- +% Version 2.510$\beta$, 2016/10/11 + +% Bugfix: We changed \@setheadheight and \@setfootheight to fix a bug +% that was introduced by the bugfix in version 2.306beta, 2009/03/28: +% If the second page has a different \textheight (because of a change +% in either headheight or footheight between pages 1 and 2), then page +% 2 would use the \textheight of page 1. Pages 3 and beyond would get +% the correct \textheight. The original version of this set \@colroom +% and \vsize to the new \textheight, but that had a bug in that if a +% float appeared at the top of a page, there would be no notice taken +% of the space lost to the float, and so the text would overrun the +% bottom of the page. The bugfix in version 2.306beta eliminated the +% changes to \@colht and \vsize. In this bugfix, we adjust \@colroom, +% \@colht, and \vsize in the same way that we adjust \textheight. + +%-------------------------------------------------------------------- +% Version 2.509$\beta$, 2016/09/12 + +% Multicolumn grade and point tables, and a new syntax for multirow +% grade and point tables (which were introduced in version +% 2.508beta). + +% Multicolumn tables are vertically oriented, while multirow tables +% are horizontally oriented, and so they do not take the optional +% argument choosing between horizontal and vertical. They all take +% one required argument specifying the number of columns (for +% multicolumn) or the number of rows (for multirow). + +% The tables can be: + +% grade tables or point tables, + +% plain, bonus, or combined, + +% indexed by questions or by pages, + +% complete or partial. + +% As usual, if you omit the optional argument that chooses between +% questions and pages, you get questions. + +% The new commands are: + +% \def\multirowgradetable{numrows}[questions or pages] +% \def\multirowpointtable{numrows}[questions or pages] +% \def\multirowbonusgradetable{numrows}[questions or pages] +% \def\multirowbonuspointtable{numrows}[questions or pages] +% \def\multirowcombinedgradetable{numrows}[questions or pages] +% \def\multirowcombinedpointtable{numrows}[questions or pages] + +% \def\multirowpartialgradetable{numrows}{rangename}[questions or pages] +% \def\multirowpartialpointtable{numrows}{rangename}[questions or pages] +% \def\multirowpartialbonusgradetable{numrows}{rangename}[questions or pages] +% \def\multirowpartialbonuspointtable{numrows}{rangename}[questions or pages] +% \def\multirowpartialcombinedgradetable{numrows}{rangename}[questions or pages] +% \def\multirowpartialcombinedpointtable{numrows}{rangename}[questions or pages] + +% \def\multicolumngradetable{numcols}[questions or pages] +% \def\multicolumnpointtable{numcols}[questions or pages] +% \def\multicolumnbonusgradetable{numcols}[questions or pages] +% \def\multicolumnbonuspointtable{numcols}[questions or pages] +% \def\multicolumncombinedgradetable{numcols}[questions or pages] +% \def\multicolumncombinedpointtable{numcols}[questions or pages] + +% \def\multicolumnpartialgradetable{numcols}{rangename}[questions or pages] +% \def\multicolumnpartialpointtable{numcols}{rangename}[questions or pages] +% \def\multicolumnpartialbonusgradetable{numcols}{rangename}[questions or pages] +% \def\multicolumnpartialbonuspointtable{numcols}{rangename}[questions or pages] +% \def\multicolumnpartialcombinedgradetable{numcols}{rangename}[questions or pages] +% \def\multicolumnpartialcombinedpointtable{numcols}{rangename}[questions or pages] + +% The older grade and point table commands can still be used. For +% example, the commands + +% \gradetable[h][questions] +% \multirowgradetable{1}[questions] + +% are equivalent. + +% The distance between the rows of a multirow table and between the +% columns of a multicolumn table is \doublerulesep, the default value +% of which is 2.0pt. You can change that using a \setlength command, +% as in + +% \setlength{\doublerulesep}{0.5in} + +%-------------------------------------------------------------------- +% Version 2.508$\beta$, 2016/08/06 + +% New commands: Multirow grade and point tables. + +% These are all horizontally oriented tables, and so do not take the +% optional argument choosing between horizontal and vertical. They all +% take one required argument specifying the number of columns, which is +% the number of columns used for the point values (including the total), +% but not counting the column of row headings. + +% Note: The syntax was changed in version 2.509beta, so that you now +% specify the number of *rows* rather than the number of *columns*! For +% example, the first command below should now be + +% \multirowgradetable{numrows}[questions or pages] + +% The tables can be: + +% grade tables or point tables, + +% plain, bonus, or combined, + +% indexed by questions or by pages, + +% complete or partial. + +% The new commands are: + + +% \multirowgradetable{numcols}[questions or pages] +% \multirowpointtable{numcols}[questions or pages] +% \multirowbonusgradetable{numcols}[questions or pages] +% \multirowbonuspointtable{numcols}[questions or pages] +% \multirowcombinedgradetable{numcols}[questions or pages] +% \multirowcombinedpointtable{numcols}[questions or pages] + +% \multirowpartialgradetable{numcols}{rangename}[questions or pages] +% \multirowpartialpointtable{numcols}{rangename}[questions or pages] +% \multirowpartialbonusgradetable{numcols}{rangename}[questions or pages] +% \multirowpartialbonuspointtable{numcols}{rangename}[questions or pages] +% \multirowpartialcombinedgradetable{numcols}{rangename}[questions or pages] +% \multirowpartialcombinedpointtable{numcols}{rangename}[questions or pages] + +%-------------------------------------------------------------------- +% Version 2.507$\beta$, 2016/07/14 + +% New commands: + +% \pointstwosided +% \pointstwosidedreversed + +% The first causes points to be in the right margin on odd numbered +% pages and in the left margin on even numbered pages. + +% The second causes points to be in the left margin on odd numbered +% pages and in the right margin on even numbered pages. + +% Also: Some minor edits (e.g., deleting the unused \thebonuspoints). + +%-------------------------------------------------------------------- +% Version 2.506$\beta$, 2016/05/12 + +% Fixed an obscure bug that arose only when \CorrectChoiceEmphasis +% used color and a \CorrectChoice (in a choices or checkboxes +% environment) followed a \choice whose text completely filled its +% last line, and which was not separated from the \CorrectChoice by a +% blank line, in which case an extra (blank) line was inserted by that +% \choice. We fixed this by adding an improvised "\leavehmode" +% (styled after \leavevmode) to the \CorrectChoice command in both the +% choices and checkboxes environments, which caused the text of the +% previous \choice to be broken into lines *before* the \special +% inserted by the \color command was added. + +%-------------------------------------------------------------------- +% Version 2.505$\beta$, 2016/05/10 + +% We fixed a bug in the choices and checkboxes environments that arose +% only when \CorrectChoiceEmphasis used color. If it did, and if the +% text of a correct choice exactly filled a line, and if there was no +% blank line in the latex file separating this correct choice from the +% following choice, there would be an extra blank line inserted after +% the correct choice. We did this by inserting \color@begingroup and +% \color@endgroup as needed. (We're pretty sure the actual fix was +% the \endgraf in the expansion of \color@endgroup.) + +%-------------------------------------------------------------------- +% Version 2.504$\beta$, 2016/05/09 + +% We fixed a bug in the solutionbox environment that caused enumerate, +% itemize, or description environments to have their text stick into +% the right margin. We did this by resetting \@totalleftmargin and +% \linewidth in the box containing the solution. + +%-------------------------------------------------------------------- +% Version 2.503$\beta$, 2016/03/25 + +% New commands: + +% \colorfillwithlines +% \colorfillwithdottedlines + +% The first causes the lines drawn by the \fillwithlines command to be +% drawn in color. The default color is set by the command + +% \definecolor{FillWithLinesColor}{gray}{0.8} + +% and the color can be changed by giving a new \definecolor command. +% You can return to black lines by giving the command + +% \nocolorfillwithlines + +% \colorfillwithdottedlines causes the lines drawn by the +% \fillwithdottedlines command to be drawn in color. The default +% color is set by the command + +% \definecolor{FillWithDottedLinesColor}{gray}{0.8} + +% and the color can be changed by giving a new \definecolor command. +% You can return to black dotted lines by giving the command + +% \nocolorfillwithdottedlines + +%-------------------------------------------------------------------- +% Version 2.502$\beta$, 2016/03/23 + +% The command + +% \colorsolutionboxes + +% that was created in version 2.501beta now affects not only the boxes +% created by \solutionbox, but also by \makeemptybox, \solutionorbox, +% and all of the boxes printed by all of the various solution +% environments when solutions are being printed surrounded by a box. + +%-------------------------------------------------------------------- +% Version 2.501$\beta$, 2016/02/08 + +% Changed the \solutionbox environment so that it works correctly +% inside a tabular. + +% Also: The \solutionbox frame can now be printed in color, as long as +% you load color.sty in the preamble. +% +% Usage: Say +% +% \usepackage{color} +% +% in the preamble, and then give the command +% +% \colorsolutionboxes +% +% to have the frame around a solutionbox in color. The default color +% was created by the command +% +% \definecolor{SolutionBoxColor}{gray}{0.8} +% +% and you can change the color by giving a new \definecolor command +% (which must be done *after* the \colorsolutionboxes command). +% +% To cancel color solutionbox frames and return to black, give the +% command +% +% \nocolorsolutionboxes + +%-------------------------------------------------------------------- % Version 2.5 2015/05/07 % No longer betatest. @@ -552,6 +810,11 @@ % Reset the effect of the most recent change: \global\advance\topmargin by -\@extrahead \global\advance\textheight by \@extrahead + % Bugfix, Version 2.510beta, 2016/10/11: + \global\advance\@colroom by \@extrahead + \global\advance\@colht by \@extrahead + \global\advance\vsize by \@extrahead + % % Save the newly set value: \def\@temp{#1} \def\@spaces{ } @@ -567,6 +830,26 @@ % Set the new values: \global\advance\topmargin by \@extrahead \global\advance\textheight by -\@extrahead + % Bugfix, Version 2.510beta, 2016/10/11: + \global\advance\@colroom by -\@extrahead + \global\advance\@colht by -\@extrahead + \global\advance\vsize by -\@extrahead + % + % Bugfix, Version 2.510beta, 2016/10/11: + % We're fixing a bug that was introduced by the bugfix in version + % 2.306beta, 2009/03/28: If the second page has a different + % \textheight (because of a change in either headheight or + % footheight between pages 1 and 2), then page 2 would use the + % \textheight of page 1. Pages 3 and beyond would get the correct + % \textheight. + % The original version of this set \@colroom and \vsize to the new + % \textheight, but that had a bug in that if a float appeared at + % the top of a page, there would be no notice taken of the space + % lost to the float, and so the text would overrun the bottom of + % the page. + % In this bugfix, we adjust \@colroom, \@colht, and \vsize in the + % same way that we adjust \textheight. + % % Make it take effect RIGHT NOW!: % (The following stuff isn't necessary if \@setheadheight is % executed only in the preamble or as we return from the output @@ -584,17 +867,23 @@ % We *do* need to put \@colht at the correct new value, though, % apparently because \@colht is set near the end of the % output routine. - \global\@colht=\textheight +% \global\@colht=\textheight +% % \global\@colroom=\textheight % \global\vsize=\textheight % \global\pagegoal=\textheight \endgroup -} +}% @setheadheight \def\@setfootheight#1{% \begingroup % Avoid trouble from using \@temp and \@spaces % Reset the effect of the most recent change: \global\advance\textheight by \@extrafoot + % Bugfix, Version 2.510beta, 2016/10/11: + \global\advance\@colroom by \@extrafoot + \global\advance\@colht by \@extrafoot + \global\advance\vsize by \@extrafoot + % % Save the newly set value: \def\@temp{#1} \def\@spaces{ } @@ -609,6 +898,26 @@ \fi % Set the new values: \global\advance\textheight by -\@extrafoot + % Bugfix, Version 2.510beta, 2016/10/11: + \global\advance\@colroom by -\@extrafoot + \global\advance\@colht by -\@extrafoot + \global\advance\vsize by -\@extrafoot + % + % Bugfix, Version 2.510beta, 2016/10/11: + % We're fixing a bug that was introduced by the bugfix in version + % 2.306beta, 2009/03/28: If the second page has a different + % \textheight (because of a change in either headheight or + % footheight between pages 1 and 2), then page 2 would use the + % \textheight of page 1. Pages 3 and beyond would get the correct + % \textheight. + % The original version of this set \@colroom and \vsize to the new + % \textheight, but that had a bug in that if a float appeared at + % the top of a page, there would be no notice taken of the space + % lost to the float, and so the text would overrun the bottom of + % the page. + % In this bugfix, we adjust \@colroom, \@colht, and \vsize in the + % same way that we adjust \textheight. + % % Make it take effect RIGHT NOW!: % (The following stuff isn't necessary if \@setfootheight is % executed only in the preamble or as we return from the output @@ -626,14 +935,13 @@ % We *do* need to put \@colht at the correct new value, though, % apparently because \@colht is set near the end of the % output routine. - \global\@colht=\textheight +% \global\@colht=\textheight +% % \global\@colroom=\textheight % \global\vsize=\textheight % \global\pagegoal=\textheight \endgroup -} - - +}% @setfootheight %--------------------------------------------------------------------- @@ -1246,7 +1554,7 @@ % the last such page with AtEndDocument. \def\numpages{\@ifundefined{exam@lastpage}% - {\mbox{\normalfont\bf ??}}% + {\mbox{\normalfont\bfseries ??}}% \exam@lastpage }% numpages @@ -1255,50 +1563,50 @@ % so that the construction \romannumeral\numcoverpages % won't generate an error on the first run of latex. \def\numcoverpages{\@ifundefined{exam@lastcoverpage}% - {0\mbox{\normalfont\bf ??}}% + {0\mbox{\normalfont\bfseries ??}}% \exam@lastcoverpage }% numpages \def\totalnumpages{\@ifundefined{exam@totalpages}% - {\mbox{\normalfont\bf ??}}% + {\mbox{\normalfont\bfseries ??}}% \exam@totalpages }% numpages \def\numpoints{\@ifundefined{exam@numpoints}% - {\mbox{\normalfont\bf ??}}% + {\mbox{\normalfont\bfseries ??}}% \exam@numpoints }% numpoints \def\numbonuspoints{\@ifundefined{exam@numbonuspoints}% - {\mbox{\normalfont\bf ??}}% + {\mbox{\normalfont\bfseries ??}}% \exam@numbonuspoints }% numbonuspoints \def\numquestions{\@ifundefined{exam@numquestions}% - {\mbox{\normalfont\bf ??}}% + {\mbox{\normalfont\bfseries ??}}% \exam@numquestions }% numquestions \def\numparts{\@ifundefined{exam@numparts}% - {\mbox{\normalfont\bf ??}}% + {\mbox{\normalfont\bfseries ??}}% \exam@numparts }% numparts \def\numsubparts{\@ifundefined{exam@numsubparts}% - {\mbox{\normalfont\bf ??}}% + {\mbox{\normalfont\bfseries ??}}% \exam@numsubparts }% numsubparts \def\numsubsubparts{\@ifundefined{exam@numsubsubparts}% - {\mbox{\normalfont\bf ??}}% + {\mbox{\normalfont\bfseries ??}}% \exam@numsubsubparts }% numsubsubparts \def\pointsofquestion#1{\@ifundefined{pointsofq@\romannumeral #1}% - {\mbox{\normalfont\bf ??}}% + {\mbox{\normalfont\bfseries ??}}% {\csname pointsofq@\romannumeral #1\endcsname}% }% pointsofquestion \def\bonuspointsofquestion#1{\@ifundefined{bonuspointsofq@\romannumeral #1}% - {\mbox{\normalfont\bf ??}}% + {\mbox{\normalfont\bfseries ??}}% {\csname bonuspointsofq@\romannumeral #1\endcsname}% }% bonuspointsofquestion @@ -1311,11 +1619,11 @@ % for that last page, and one of those will be undefined. % % \def\pointsonpage#1{\@ifundefined{pointsonpage@\romannumeral #1}% -% {\mbox{\normalfont\bf ??}}% +% {\mbox{\normalfont\bfseries ??}}% % {\csname pointsonpage@\romannumeral #1\endcsname}% % }% pointsonpage % \def\bonuspointsonpage#1{\@ifundefined{bonuspointsonpage@\romannumeral #1}% -% {\mbox{\normalfont\bf ??}}% +% {\mbox{\normalfont\bfseries ??}}% % {\csname bonuspointsonpage@\romannumeral #1\endcsname}% % }% bonuspointsonpage % @@ -2125,6 +2433,18 @@ \fi }% add@half +% Important reminder about \ifhlfcntr@pos: Do not use it inside +% another conditional! The construction +% \ifhlfcntr@pos{somecounter} +% do some stuff... +% \fi +% is perfectly fine as long as it's expanded, but: If it's inside +% another conditional, and the condition is not satisfied, then it's +% read through without expansion. In that case, TeX sees the +% \ifhlfcntr@pos but does *not* recognize it as being part of a +% conditional, but when it sees the concluding \fi it does recognize +% that, and so TeX completes the outer conditional at that \fi, which +% causes an error. \newcounter{ifpos@cntr} \def\ifhlfcntr@pos#1{% % The argument must be a hlfcntr (which, of course, @@ -3132,8 +3452,8 @@ \def\@checkqueslevel#1{% \begingroup - \def\@temp{#1}% - \ifx\@temp\@queslevel + \def\exam@temp{#1}% + \ifx\exam@temp\@queslevel % Everything's fine; do nothing. \else \ClassError{exam}{% @@ -3339,10 +3659,10 @@ \def\setup@point@toks{% % We set the token list \point@toks equal to the sequence of commands -% needed to put \padded@point@block at the correct location, followed by the -% tokens ``\global \point@toks={}''. The \question, \part, \subpart, -% or \subsubpart command then adds the two tokens ``\the\point@toks'' -% to \everypar. +% needed to put \padded@point@block at the correct location, followed +% by the tokens ``\global \point@toks={}''. The \question, \part, +% \subpart, or \subsubpart command then adds the two tokens +% ``\the\point@toks'' to \everypar. % % Note: It is not the *contents* of \point@toks that is added to % \everypar; just the two tokens ``\the\point@toks''. This difference @@ -3362,6 +3682,66 @@ % tokens ``\the\point@toks'' will insert an *empty* token list, which % will do no harm. % + \if@pointstwosided + % Set \csname \q@label \endcsname equal to the thing + % that expands to the page number of the current (question or + % part or subpart or subsubpar; whatever it is), but do it + % carefully because, if we don't yet have page info, then it won't + % be defined: + \ifx\@queslevel\ques@ref + \def\q@label{Pg@question@\arabic{question}} + \else + \ifx\@queslevel\part@ref + \def\q@label{Pg@part@\arabic{question}@\arabic{partno}} + \else + \ifx\@queslevel\subpart@ref + \def\q@label{Pg@subpart@\arabic{question}% + @\arabic{partno}@\arabic{subpart}} + \else + \ifx\@queslevel\subsubpart@ref + \def\q@label{Pg@subsubpart@\arabic{question}% + @\arabic{partno}@\arabic{subpart}@\arabic{subsubpart}} + \else + \ClassError{exam}{% + This can't happen in function \protect\setup@point@toks + \MessageBreak + }{% + An unexplained error occurred in exam.cls;\MessageBreak + please inform the package maintainer, and send along + \MessageBreak + the LaTeX file that shows the error.\MessageBreak + }% + \fi + \fi + \fi + \fi + % + \expandafter\ifx \csname \q@label \endcsname\relax + % No page info yet; put it into the right margin + \@pointsinrightmargintrue + \@pointsinleftmarginfalse + \else + \ifodd \csname \q@label \endcsname\relax + \if@pointsinoutsidemargin + \@pointsinrightmargintrue + \@pointsinleftmarginfalse + \else + \@pointsinrightmarginfalse + \@pointsinleftmargintrue + \fi + \else + \if@pointsinoutsidemargin + \@pointsinrightmarginfalse + \@pointsinleftmargintrue + \else + \@pointsinrightmargintrue + \@pointsinleftmarginfalse + \fi + \fi + \fi + \fi + % That ends the \if@pointstwosided. + % Now we actually setup \point@toks: \if@pointsinleftmargin \point@toks={% \llap{\padded@point@block @@ -3392,8 +3772,6 @@ \fi }% setup@point@toks - - \def\droppoints{% \leavevmode\unskip\nobreak\hfill \rlap{\hskip\rightmargin % Defined by the list environment @@ -3404,7 +3782,6 @@ \par } - \def\droptotalpoints{% \leavevmode\unskip\nobreak\hfill \rlap{\hskip\rightmargin % Defined by the list environment @@ -3476,29 +3853,55 @@ \newif\if@pointsdropped \newif\if@pointsinleftmargin \newif\if@pointsinrightmargin +\newif\if@pointstwosided +\newif\if@pointsinoutsidemargin + +% If we have \@pointstwosidedtrue and \@pointsinoutsidemarginfalse, +% then the points will be printed on the inside margin (left on odd +% numbered pages, right on even numbered pages). If we have +% \@pointstwosidedfalse, then \if@pointsinoutsidemargin is ignored. + +% If we have \@pointstwosidedtrue, then both \@pointsinleftmargin and +% \@pointsinrightmargin will be flipped back and forth, as needed, in +% \setup@point@toks. + \def\pointsinleftmargin{\global\@pointsinleftmargintrue \global\@pointsinrightmarginfalse \global\@pointsdroppedfalse + \global\@pointstwosidedfalse \gdef\pt@name{\@marginpointname}% \gdef\bnspt@name{\@marginbonuspointname}} \def\pointsinrightmargin{\global\@pointsinrightmargintrue \global\@pointsinleftmarginfalse \global\@pointsdroppedfalse + \global\@pointstwosidedfalse \gdef\pt@name{\@marginpointname}% \gdef\bnspt@name{\@marginbonuspointname}} \def\nopointsinmargin{\global\@pointsinleftmarginfalse \global\@pointsinrightmarginfalse \global\@pointsdroppedfalse + \global\@pointstwosidedfalse \gdef\pt@name{\@pointname}% \gdef\bnspt@name{\@bonuspointname}} \def\pointsdroppedatright{\global\@pointsdroppedtrue \global\@pointsinleftmarginfalse \global\@pointsinrightmarginfalse + \global\@pointstwosidedfalse \gdef\pt@name{\@marginpointname}% \gdef\bnspt@name{\@marginbonuspointname}} -\let\pointsinmargin\pointsinleftmargin -\let\nopointsinrightmargin\nopointsinmargin -\let\nopointsinleftmargin\nopointsinmargin +\def\pointstwosided{\global\@pointstwosidedtrue + \global\@pointsinoutsidemargintrue + \global\@pointsdroppedfalse + \gdef\pt@name{\@marginpointname}% + \gdef\bnspt@name{\@marginbonuspointname}} +\def\pointstwosidedreversed{\global\@pointstwosidedtrue + \global\@pointsinoutsidemarginfalse + \global\@pointsdroppedfalse + \gdef\pt@name{\@marginpointname}% + \gdef\bnspt@name{\@marginbonuspointname}} +\let\pointsinmargin=\pointsinleftmargin +\let\nopointsinrightmargin=\nopointsinmargin +\let\nopointsinleftmargin=\nopointsinmargin \nopointsinmargin @@ -3534,7 +3937,6 @@ % which confines the effects of anything here: \gdef\bonuspoint@block{#1}% } -\def\thebonuspoints{\@points \@bonuspointname} %Initialize: \noboxedpoints @@ -3688,6 +4090,7 @@ \settowidth{\leftmargin}{W.\hskip\labelsep\hskip 2.5em}% \def\choice{% \if@correctchoice + \color@endgroup \endgroup \fi \item @@ -3695,12 +4098,30 @@ } % choice \def\CorrectChoice{% \if@correctchoice + \color@endgroup \endgroup \fi \ifprintanswers % We can't say \choice here, because that would % insert an \endgroup: - \begingroup \@correctchoicetrue + % 2016/05/10: We say \color@begingroup in addition to + % \begingroup in case \CorrectChoiceEmphasis involves color + % and the text exactly fills the line (which would + % otherwise create a blank line after this choice): + % 2016/05/11: We leave hmode if we're in it, + % i.e., if there's no blank line preceding this + % \CorrectChoice command. (Without this, the + % \special created by a \color{whatever} command that might + % be inserted by \CorrectChoice@Emphasis would be appended + % to the previous \choice, which could cause an extra + % (blank) line to be inserted before this \CorrectChoice.) + % Since \par and \endgraf seem to cancel \@totalleftmargin + % (for reasons I don't understand), we'll do the following: + % Motivated by the def of \leavevmode, + % \def\leavevmode{\unhbox\voidb@x} + % we will now leave hmode (if we're in hmode): + \ifhmode \unskip\unskip\unvbox\voidb@x \fi + \begingroup \color@begingroup \@correctchoicetrue \CorrectChoice@Emphasis \fi \item @@ -3713,7 +4134,7 @@ \choiceshook }% }% - {\if@correctchoice \endgroup \fi \endlist} + {\if@correctchoice \color@endgroup \endgroup \fi \endlist} \newenvironment{oneparchoices}% {% @@ -3768,19 +4189,36 @@ \settowidth{\leftmargin}{W.\hskip\labelsep\hskip 2.5em}% \def\choice{% \if@correctchoice - \endgroup + \color@endgroup \endgroup \fi \item \do@choice@pageinfo } % choice \def\CorrectChoice{% \if@correctchoice - \endgroup + \color@endgroup \endgroup \fi \ifprintanswers % We can't say \choice here, because that would - % insert an \endgroup: - \begingroup \@correctchoicetrue + % insert an \endgroup. + % 2016/05/10: We say \color@begingroup in addition to + % \begingroup in case \CorrectChoiceEmphasis involves color + % and the text exactly fills the line (which would + % otherwise create a blank line after this choice): + % 2016/05/11: We leave hmode if we're in it, + % i.e., if there's no blank line preceding this + % \CorrectChoice command. (Without this, the + % \special created by a \color{whatever} command that might + % be inserted by \CorrectChoice@Emphasis would be appended + % to the previous \choice, which could cause an extra + % (blank) line to be inserted before this \CorrectChoice.) + % Since \par and \endgraf seem to cancel \@totalleftmargin + % (for reasons I don't understand), we'll do the following: + % Motivated by the def of \leavevmode, + % \def\leavevmode{\unhbox\voidb@x} + % we will now leave hmode (if we're in hmode): + \ifhmode \unskip\unskip\unvbox\voidb@x \fi + \begingroup \color@begingroup \@correctchoicetrue \CorrectChoice@Emphasis \item[\checked@char] \else @@ -3795,7 +4233,7 @@ \checkboxeshook }% }% - {\if@correctchoice \endgroup \fi \endlist} + {\if@correctchoice \color@endgroup \endgroup \fi \endlist} \newenvironment{oneparcheckboxes}% {% @@ -3851,7 +4289,8 @@ %-------------------------------------------------------------------- % Answer Lines (for short answer questions) -% Note: \ques@ref is also used in \item@points@pageinfo +% Note: \ques@ref is also used in \item@points@pageinfo, and all four +% of the following are used in \setup@point@toks \def\ques@ref{question} \def\part@ref{part} @@ -3894,7 +4333,10 @@ \ifprintanswers \ans@l~\hbox to 0pt{\hbox to \answerlinelength{\hrulefill}\hss}% \raise \answerclearance\hbox to \answerlinelength{% - \CorrectChoice@Emphasis \hfil #1\hss}% + % 2016/05/10: Added \color@begingroup and \color@endgroup: + \color@begingroup + \CorrectChoice@Emphasis \hfil #1\hss + \color@endgroup}% \else \ans@l~\hbox to \answerlinelength{\hrulefill}% \fi @@ -3961,9 +4403,11 @@ \setbox0 \hbox{\color@begingroup \CorrectChoice@Emphasis \fillin@ans \color@endgroup}% \ifdim\wd0 > #1\relax - \hbox{\CorrectChoice@Emphasis \fillin@ans}% + \hbox{\color@begingroup\CorrectChoice@Emphasis \fillin@ans + \color@endgroup}% \else - \hbox to #1{\CorrectChoice@Emphasis \hfil \fillin@ans \hfil}% + \hbox to #1{\color@begingroup\CorrectChoice@Emphasis + \hfil \fillin@ans \hfil\color@endgroup}% \fi \endgroup \else @@ -4043,18 +4487,71 @@ % \setlength\linefillthickness{.1pt} % % This value can be changed by giving a new \setlength command. - +% +% As of version 2.503, 2016/03/25, the lines drawn by the +% \fillwithlines command will be drawn in color if the user has given +% the command +% +% \colorfillwithlines. +% +% The actual drawing of the lines is now done by the command +% \do@fillwithlines, after the \fillwithlines command decides whether +% they will be in color. The default color is set by the command +% +% \definecolor{FillWithLinesColor}{gray}{0.8} +% +% and the color can be changed by giving a new \definecolor command. +% You can return to black lines by giving the command +% +% \nocolorfillwithlines \newlength\linefillheight \newlength\linefillthickness \setlength\linefillheight{.25in} \setlength\linefillthickness{0.1pt} + + + +\newif\if@colorfillwithlines +\@colorfillwithlinesfalse +\def\colorfillwithlines{% + \@ifundefined{definecolor} + {% + \ClassError{exam}{% + You must load the color package with the command\MessageBreak + \space\space\protect\usepackage{color}\MessageBreak + in order to use the command \protect\colorfillwithlines + \MessageBreak + }{% + This command makes use of the package color.sty,\MessageBreak + and so you have to load color.sty before your\MessageBreak + \protect\begin{document} command.\MessageBreak + }% + }% + {% + \definecolor{FillWithLinesColor}{gray}{0.8} + \@colorfillwithlinestrue + }% +}% \colorfillwithlines +\def\nocolorfillwithlines{\@colorfillwithlinesfalse} + +\newcommand\fillwithlines[1]{% + \if@colorfillwithlines + \color@begingroup + \color{FillWithLinesColor}% + \do@fillwithlines{#1}% + \color@endgroup + \else + \do@fillwithlines{#1}% + \fi +}% \fillwithlines + \newcommand\linefill{\leavevmode \leaders\hrule height \linefillthickness \hfill\kern\z@} - -\def\fillwithlines#1{% +% \do@fillwithlines is called only by \fillwithlines +\def\do@fillwithlines#1{% \begingroup \ifhmode \par @@ -4069,7 +4566,7 @@ % no matter where on the page it happens to start: \cleaders \copy0 \vskip #1 \hbox{}% \endgroup -} +}% \do@fillwithlines %-------------------------------------------------------------------- % \fillwithdottedlines @@ -4093,10 +4590,64 @@ % % This value can be changed by giving a new \setlength command. +% As of version 2.503, 2016/03/25, the dotted lines drawn by the +% \fillwithdottedlines command will be drawn in color if the user has +% given the command +% +% \colorfillwithdottedlines. +% +% The actual drawing of the lines is now done by the command +% \do@fillwithdottedlines, after the \fillwithdottedlines command +% decides whether they will be in color. The default color is set by +% the command +% +% \definecolor{FillWithDottedLinesColor}{gray}{0.8} +% +% and the color can be changed by giving a new \definecolor command. +% You can return to black lines by giving the command +% +% \nocolorfillwithdottedlines + + \newlength\dottedlinefillheight \setlength\dottedlinefillheight{.25in} -\def\fillwithdottedlines#1{% +\newif\if@colorfillwithdottedlines +\@colorfillwithdottedlinesfalse +\def\colorfillwithdottedlines{% + \@ifundefined{definecolor} + {% + \ClassError{exam}{% + You must load the color package with the command\MessageBreak + \space\space\protect\usepackage{color}\MessageBreak + in order to use the command \protect\colorfillwithdottedlines + \MessageBreak + }{% + This command makes use of the package color.sty,\MessageBreak + and so you have to load color.sty before your\MessageBreak + \protect\begin{document} command.\MessageBreak + }% + }% + {% + \definecolor{FillWithDottedLinesColor}{gray}{0.8} + \@colorfillwithdottedlinestrue + }% +}% \colorfillwithdottedlines +\def\nocolorfillwithdottedlines{\@colorfillwithdottedlinesfalse} + +\newcommand\fillwithdottedlines[1]{% + \if@colorfillwithdottedlines + \color@begingroup + \color{FillWithDottedLinesColor}% + \do@fillwithdottedlines{#1}% + \color@endgroup + \else + \do@fillwithdottedlines{#1}% + \fi +}% \fillwithdottedlines + +% \do@fillwithdottedlines is called only by \fillwithdottedlines +\def\do@fillwithdottedlines#1{% \begingroup \ifhmode \par @@ -4111,7 +4662,7 @@ % no matter where on the page it happens to start: \cleaders \copy0 \vskip #1 \hbox{}% \endgroup -}% fillwithdottedlines +}% \do@fillwithdottedlines %-------------------------------------------------------------------- % \fillwithgrid @@ -4261,7 +4812,26 @@ \newlength\minboxheight \setlength\minboxheight{.1in} + +% As of version 2.502, 2016/03/23, the frame drawn by the +% \makeemptybox command will be drawn in color if the user has given +% the command \colorsolutionboxes. The actual drawing of the box is +% now done by the command \do@emptybox, after the \makeemptybox +% command decides whether it will be in color. + \newcommand\makeemptybox[1]{% + \if@colorsolutionboxes + \color@begingroup + \color{SolutionBoxColor}% + \do@emptybox{#1}% + \color@endgroup + \else + \do@emptybox{#1}% + \fi +} + +% The command \do@emptybox is called only by \makeemptybox. +\newcommand\do@emptybox[1]{% \par \hbox to \hsize{\hskip\@totalleftmargin \leaders\hrule\hfill}% \nointerlineskip @@ -4476,15 +5046,44 @@ \cvsword{Score} \cvtword{Total:} - -% The only commands here accessible to the user are \gradetable, -% \bonusgradetable, \combinedgradetable, \pointtable, -% \bonuspointtable, \combinedpointtable, \partialgradetable, +% Before we created multirow and multicolumn tables, he only commands +% here accessible to the user were \gradetable, \bonusgradetable, +% \combinedgradetable, \pointtable, \bonuspointtable, +% \combinedpointtable, \partialgradetable, % \partialbonusgradetable, \partialcombinedtable, \partialpointtable, % \partialbonuspointtable, \partialcombinedpointtable, % \begingradingrange, \endgradingrange, \pointsinrange, % \bonuspointsinrange, \firstqinrange, \lastqinrange, and -% \numqinrange. +% \numqinrange. The new user commands are +% +% \def\multirowgradetable +% \def\multirowpointtable +% \def\multirowbonusgradetable +% \def\multirowbonuspointtable +% \def\multirowcombinedgradetable +% \def\multirowcombinedpointtable +% +% \def\multirowpartialgradetable +% \def\multirowpartialpointtable +% \def\multirowpartialbonusgradetable +% \def\multirowpartialbonuspointtable +% \def\multirowpartialcombinedgradetable +% \def\multirowpartialcombinedpointtable +% +% \def\multicolumngradetable +% \def\multicolumnpointtable +% \def\multicolumnbonusgradetable +% \def\multicolumnbonuspointtable +% \def\multicolumncombinedgradetable +% \def\multicolumncombinedpointtable +% +% \def\multicolumnpartialgradetable +% \def\multicolumnpartialpointtable +% \def\multicolumnpartialbonusgradetable +% \def\multicolumnpartialbonuspointtable +% \def\multicolumnpartialcombinedgradetable +% \def\multicolumnpartialcombinedpointtable + % The possibilities are @@ -4559,6 +5158,35 @@ % \numqinrange{whatever} % % where ``whatever'' is a label chosen by the user. +% +% \def\multirowgradetable{numcols}[questions or pages] +% \def\multirowpointtable{numcols}[questions or pages] +% \def\multirowbonusgradetable{numcols}[questions or pages] +% \def\multirowbonuspointtable{numcols}[questions or pages] +% \def\multirowcombinedgradetable{numcols}[questions or pages] +% \def\multirowcombinedpointtable{numcols}[questions or pages] +% +% \def\multirowpartialgradetable{numcols}{rangename}[questions or pages] +% \def\multirowpartialpointtable{numcols}{rangename}[questions or pages] +% \def\multirowpartialbonusgradetable{numcols}{rangename}[questions or pages] +% \def\multirowpartialbonuspointtable{numcols}{rangename}[questions or pages] +% \def\multirowpartialcombinedgradetable{numcols}{rangename}[questions or pages] +% \def\multirowpartialcombinedpointtable{numcols}{rangename}[questions or pages] +% +% \def\multicolumngradetable{numrows}[questions or pages] +% \def\multicolumnpointtable{numrows}[questions or pages] +% \def\multicolumnbonusgradetable{numrows}[questions or pages] +% \def\multicolumnbonuspointtable{numrows}[questions or pages] +% \def\multicolumncombinedgradetable{numrows}[questions or pages] +% \def\multicolumncombinedpointtable{numrows}[questions or pages] + +% \def\multicolumnpartialgradetable{numrows}{rangename}[questions or pages] +% \def\multicolumnpartialpointtable{numrows}{rangename}[questions or pages] +% \def\multicolumnpartialbonusgradetable{numrows}{rangename}[questions or pages] +% \def\multicolumnpartialbonuspointtable{numrows}{rangename}[questions or pages] +% \def\multicolumnpartialcombinedgradetable{numrows}{rangename}[questions or pages] +% \def\multicolumnpartialcombinedpointtable{numrows}{rangename}[questions or pages] + % If one or both optional arguments are omitted, the defaults are % `[v]' and `[questions]'. @@ -4573,8 +5201,9 @@ % \partialcombinedpointtable: \newif\if@partial -% \@combinedtrue means we're doing \combinedgradetable -% or \combinedpointtable: +% \@combinedtrue means we're doing \combinedgradetable, +% \combinedpointtable, \partialcombinedgradetable, or +% \partialcombinedpointtable: \newif\if@combined % It's OK to use the counter num@cols as a scratch counter @@ -4606,8 +5235,6 @@ }% endgradingrange - - % Now that grading tables may be for only part of the exam, % we need the counter tbl@points to add up the total points % for the questions (or pages) that appear on the table: @@ -4617,6 +5244,248 @@ % points for the questions (or pages) that appear on the table: \new@hlfcntr{tbl@bonuspoints} +%-------------------------------------------------------------------- +% multirow tables, non-partial: + +\def\multirowgradetable#1{% + \@scorestrue + \@bonusfalse + \@partialfalse + \@combinedfalse + \setcounter{num@rows}{#1}% + \i@gtable[h]% +} + +\def\multirowpointtable#1{% + \@scoresfalse + \@bonusfalse + \@partialfalse + \@combinedfalse + \setcounter{num@rows}{#1}% + \i@gtable[h]% +} + +\def\multirowbonusgradetable#1{% + \@scorestrue + \@bonustrue + \@partialfalse + \@combinedfalse + \setcounter{num@rows}{#1}% + \i@gtable[h]% +} + +\def\multirowbonuspointtable#1{% + \@scoresfalse + \@bonustrue + \@partialfalse + \@combinedfalse + \setcounter{num@rows}{#1}% + \i@gtable[h]% +} + +\def\multirowcombinedgradetable#1{% + \@scorestrue + \@bonusfalse + \@partialfalse + \@combinedtrue + \setcounter{num@rows}{#1}% + \i@gtable[h]% +} + +\def\multirowcombinedpointtable#1{% + \@scoresfalse + \@bonusfalse + \@partialfalse + \@combinedtrue + \setcounter{num@rows}{#1}% + \i@gtable[h]% +} + +%-------------------------------------------------------------------- +% multirow tables, partial: + +\def\multirowpartialgradetable#1#2{% + \@scorestrue + \@bonusfalse + \@partialtrue + \@combinedfalse + \def\tbl@range{#2}% + \setcounter{num@rows}{#1}% + \i@gtable[h]% +} + +\def\multirowpartialpointtable#1#2{% + \@scoresfalse + \@bonusfalse + \@partialtrue + \@combinedfalse + \def\tbl@range{#2}% + \setcounter{num@rows}{#1}% + \i@gtable[h]% +} + +\def\multirowpartialbonusgradetable#1#2{% + \@scorestrue + \@bonustrue + \@partialtrue + \@combinedfalse + \def\tbl@range{#2}% + \setcounter{num@rows}{#1}% + \i@gtable[h]% +} + +\def\multirowpartialbonuspointtable#1#2{% + \@scoresfalse + \@bonustrue + \@partialtrue + \@combinedfalse + \def\tbl@range{#2}% + \setcounter{num@rows}{#1}% + \i@gtable[h]% +} + +\def\multirowpartialcombinedgradetable#1#2{% + \@scorestrue + \@bonusfalse + \@partialtrue + \@combinedtrue + \def\tbl@range{#2}% + \setcounter{num@rows}{#1}% + \i@gtable[h]% +} + +\def\multirowpartialcombinedpointtable#1#2{% + \@scoresfalse + \@bonusfalse + \@partialtrue + \@combinedtrue + \def\tbl@range{#2}% + \setcounter{num@rows}{#1}% + \i@gtable[h]% +} + +%-------------------------------------------------------------------- +% multicolumn tables, non-partial: + +\def\multicolumngradetable#1{% + \@scorestrue + \@bonusfalse + \@partialfalse + \@combinedfalse + \setcounter{num@cols}{#1}% + \i@gtable[v]% +} + +\def\multicolumnpointtable#1{% + \@scoresfalse + \@bonusfalse + \@partialfalse + \@combinedfalse + \setcounter{num@cols}{#1}% + \i@gtable[v]% +} + +\def\multicolumnbonusgradetable#1{% + \@scorestrue + \@bonustrue + \@partialfalse + \@combinedfalse + \setcounter{num@cols}{#1}% + \i@gtable[v]% +} + +\def\multicolumnbonuspointtable#1{% + \@scoresfalse + \@bonustrue + \@partialfalse + \@combinedfalse + \setcounter{num@cols}{#1}% + \i@gtable[v]% +} + +\def\multicolumncombinedgradetable#1{% + \@scorestrue + \@bonusfalse + \@partialfalse + \@combinedtrue + \setcounter{num@cols}{#1}% + \i@gtable[v]% +} + +\def\multicolumncombinedpointtable#1{% + \@scoresfalse + \@bonusfalse + \@partialfalse + \@combinedtrue + \setcounter{num@cols}{#1}% + \i@gtable[v]% +} + +%-------------------------------------------------------------------- +% multicolumn tables, partial: + +\def\multicolumnpartialgradetable#1#2{% + \@scorestrue + \@bonusfalse + \@partialtrue + \@combinedfalse + \def\tbl@range{#2}% + \setcounter{num@cols}{#1}% + \i@gtable[v]% +} + +\def\multicolumnpartialpointtable#1#2{% + \@scoresfalse + \@bonusfalse + \@partialtrue + \@combinedfalse + \def\tbl@range{#2}% + \setcounter{num@cols}{#1}% + \i@gtable[v]% +} + +\def\multicolumnpartialbonusgradetable#1#2{% + \@scorestrue + \@bonustrue + \@partialtrue + \@combinedfalse + \def\tbl@range{#2}% + \setcounter{num@cols}{#1}% + \i@gtable[v]% +} + +\def\multicolumnpartialbonuspointtable#1#2{% + \@scoresfalse + \@bonustrue + \@partialtrue + \@combinedfalse + \def\tbl@range{#2}% + \setcounter{num@cols}{#1}% + \i@gtable[v]% +} + +\def\multicolumnpartialcombinedgradetable#1#2{% + \@scorestrue + \@bonusfalse + \@partialtrue + \@combinedtrue + \def\tbl@range{#2}% + \setcounter{num@cols}{#1}% + \i@gtable[v]% +} + +\def\multicolumnpartialcombinedpointtable#1#2{% + \@scoresfalse + \@bonusfalse + \@partialtrue + \@combinedtrue + \def\tbl@range{#2}% + \setcounter{num@cols}{#1}% + \i@gtable[v]% +} + +%-------------------------------------------------------------------- +% partial single row (and column) tables: \def\partialgradetable#1{% \@scorestrue @@ -4624,6 +5493,12 @@ \@partialtrue \@combinedfalse \def\tbl@range{#1}% + % We don't yet know if the table is vertical or horizontal, and so + % we don't know if we need to set num@cols or num@rows. We'll set + % them both, and we'll later on just ignore the value of the one + % that we didn't need to set here: + \setcounter{num@cols}{1}% + \setcounter{num@rows}{1}% % If the user doesn't include the optional argument % choosing between vertical and horizontal, % we give them vertical: @@ -4636,6 +5511,12 @@ \@partialtrue \@combinedfalse \def\tbl@range{#1}% + % We don't yet know if the table is vertical or horizontal, and so + % we don't know if we need to set num@cols or num@rows. We'll set + % them both, and we'll later on just ignore the value of the one + % that we didn't need to set here: + \setcounter{num@cols}{1}% + \setcounter{num@rows}{1}% % If the user doesn't include the optional argument % choosing between vertical and horizontal, % we give them vertical: @@ -4648,6 +5529,12 @@ \@partialtrue \@combinedtrue \def\tbl@range{#1}% + % We don't yet know if the table is vertical or horizontal, and so + % we don't know if we need to set num@cols or num@rows. We'll set + % them both, and we'll later on just ignore the value of the one + % that we didn't need to set here: + \setcounter{num@cols}{1}% + \setcounter{num@rows}{1}% % If the user doesn't include the optional argument % choosing between vertical and horizontal, % we give them vertical: @@ -4660,6 +5547,12 @@ \@partialtrue \@combinedfalse \def\tbl@range{#1}% + % We don't yet know if the table is vertical or horizontal, and so + % we don't know if we need to set num@cols or num@rows. We'll set + % them both, and we'll later on just ignore the value of the one + % that we didn't need to set here: + \setcounter{num@cols}{1}% + \setcounter{num@rows}{1}% % If the user doesn't include the optional argument % choosing between vertical and horizontal, % we give them vertical: @@ -4672,6 +5565,12 @@ \@partialtrue \@combinedfalse \def\tbl@range{#1}% + % We don't yet know if the table is vertical or horizontal, and so + % we don't know if we need to set num@cols or num@rows. We'll set + % them both, and we'll later on just ignore the value of the one + % that we didn't need to set here: + \setcounter{num@cols}{1}% + \setcounter{num@rows}{1}% % If the user doesn't include the optional argument % choosing between vertical and horizontal, % we give them vertical: @@ -4684,6 +5583,12 @@ \@partialtrue \@combinedtrue \def\tbl@range{#1}% + % We don't yet know if the table is vertical or horizontal, and so + % we don't know if we need to set num@cols or num@rows. We'll set + % them both, and we'll later on just ignore the value of the one + % that we didn't need to set here: + \setcounter{num@cols}{1}% + \setcounter{num@rows}{1}% % If the user doesn't include the optional argument % choosing between vertical and horizontal, % we give them vertical: @@ -4691,12 +5596,19 @@ }% partialcombinedpointtable %-------------------------------------------------------------------- +% single row (and column) tables, non-partial: \def\gradetable{% \@scorestrue \@bonusfalse \@partialfalse \@combinedfalse + % We don't yet know if the table is vertical or horizontal, and so + % we don't know if we need to set num@cols or num@rows. We'll set + % them both, and we'll later on just ignore the value of the one + % that we didn't need to set here: + \setcounter{num@cols}{1}% + \setcounter{num@rows}{1}% % If the user doesn't include the optional argument % choosing between vertical and horizontal, % we give them vertical: @@ -4708,6 +5620,12 @@ \@bonustrue \@partialfalse \@combinedfalse + % We don't yet know if the table is vertical or horizontal, and so + % we don't know if we need to set num@cols or num@rows. We'll set + % them both, and we'll later on just ignore the value of the one + % that we didn't need to set here: + \setcounter{num@cols}{1}% + \setcounter{num@rows}{1}% % If the user doesn't include the optional argument % choosing between vertical and horizontal, % we give them vertical: @@ -4719,6 +5637,12 @@ \@bonusfalse \@partialfalse \@combinedtrue + % We don't yet know if the table is vertical or horizontal, and so + % we don't know if we need to set num@cols or num@rows. We'll set + % them both, and we'll later on just ignore the value of the one + % that we didn't need to set here: + \setcounter{num@cols}{1}% + \setcounter{num@rows}{1}% % If the user doesn't include the optional argument % choosing between vertical and horizontal, % we give them vertical: @@ -4730,6 +5654,12 @@ \@bonusfalse \@partialfalse \@combinedfalse + % We don't yet know if the table is vertical or horizontal, and so + % we don't know if we need to set num@cols or num@rows. We'll set + % them both, and we'll later on just ignore the value of the one + % that we didn't need to set here: + \setcounter{num@cols}{1}% + \setcounter{num@rows}{1}% % If the user doesn't include the optional argument % choosing between vertical and horizontal, % we give them vertical: @@ -4741,6 +5671,12 @@ \@bonustrue \@partialfalse \@combinedfalse + % We don't yet know if the table is vertical or horizontal, and so + % we don't know if we need to set num@cols or num@rows. We'll set + % them both, and we'll later on just ignore the value of the one + % that we didn't need to set here: + \setcounter{num@cols}{1}% + \setcounter{num@rows}{1}% % If the user doesn't include the optional argument % choosing between vertical and horizontal, % we give them vertical: @@ -4752,6 +5688,12 @@ \@bonusfalse \@partialfalse \@combinedtrue + % We don't yet know if the table is vertical or horizontal, and so + % we don't know if we need to set num@cols or num@rows. We'll set + % them both, and we'll later on just ignore the value of the one + % that we didn't need to set here: + \setcounter{num@cols}{1}% + \setcounter{num@rows}{1}% % If the user doesn't include the optional argument % choosing between vertical and horizontal, % we give them vertical: @@ -4764,7 +5706,7 @@ % (the defaults being [v] and [questions]) and then make sure % that the user said \addpoints and that this isn't the % first run of LaTeX. -% \do@table then branches, depending on whether the user +% \find@p@or@q@range then branches, depending on whether the user % selected [questions] or [pages]. \def\i@gtable[#1]{% @@ -4774,10 +5716,11 @@ \@ifnextchar[{\ii@gtable{#1}}{\ii@gtable{#1}[questions]}% } \def\ii@gtable#1[#2]{% + % We get here from \i@gtable. % We make sure the user said \addpoints, and then make sure % that this isn't the first run of LaTeX (by checking that % \exam@numpoints is defined). If both of those are OK, - % we go on to \do@table to see whether we're doing a table + % we go to \find@p@or@q@range to see whether we're doing a table % indexed by questions or by pages. \if@addpoints \@ifundefined{exam@numpoints}% @@ -4788,7 +5731,7 @@ }% \fbox{Run \LaTeX{} again to produce the table}% }% - {\do@table{#1}{#2}}% + {\find@p@or@q@range{#1}{#2}}% \else \ClassError{exam}{% You must give the command \protect\addpoints\MessageBreak @@ -4801,46 +5744,50 @@ \fi }% ii@gtable - \def\@questionsref{questions} \def\@pagesref{pages} -\def\do@table#1#2{% - % The first argument is ``v'' or ``h''; - % the second argument is ``questions'' or ``pages''. +\def\find@p@or@q@range#1#2{% + % We get here from \ii@gtable. + % The first argument should be ``v'' or ``h''; + % the second argument should be ``questions'' or ``pages''. % See whether we're doing a table indexed by - % questions or by pages: - \begingroup % avoid trouble from using \@temp - \def\@temp{#2}% - \ifx\@temp\@questionsref + % questions (in which case we go to \find@qrange) or by pages (in + % which case we go to \find@prange): + \begingroup + % We've begun a group that will contain the construction of the + % table, to confine the effect of any \def's that we use. + \def\exam@temp{#2}% + \ifx\exam@temp\@questionsref + \tbl@pgsfalse \find@qrange{#1}% \else - \ifx\@temp\@pagesref + \ifx\exam@temp\@pagesref + \tbl@pgstrue \find@prange{#1}% \else \ClassError{exam}{% - The second optional argument to a\MessageBreak - \space\space grade table or point table command\MessageBreak - \space \space must be either `questions' or - `pages',\MessageBreak - \space\space not `#2'.\MessageBreak + Grade and point tables can be indexed\MessageBreak + \space\space by either `questions' or `pages',\MessageBreak + \space\space but not by `#2'.\MessageBreak }{% Grade tables and point tables can be indexed by questions or pages;\MessageBreak \space\space for others, you're on your own.\MessageBreak }% - \fbox{Error: grade or point table: Invalid second - optional argument `#2'.}% + \fbox{\textbf{Error:} grade or point table: Invalid argument + `#2' must be `questions' or `pages'.}% \fi \fi \endgroup -}% do@table - +}% find@p@or@q@range +% \range@undefined can be called from either \find@qrange or +% \find@prange \def\range@undefined{% \fbox{Warning: grading range `\tbl@range ' not defined; run \LaTeX{} again.}% \ClassWarning{exam}{% - Grading range `\tbl@range ' not defined.\MessageBreak + Grading range `\tbl@range' not defined.\MessageBreak \space\space Run LaTeX again to produce the table.\MessageBreak }% }% range@undefined @@ -4850,16 +5797,18 @@ %-------------------------------------------------------------------- % Grade and point tables indexed by question numbers: -% We get to \find@qrange if we know we're doing a table indexed -% by question numbers. The argument is either ``v'' or ``h''. -% If we're not doing a partial table, then \find@qrange sets -% \tbl@firstq to 1 and \tbl@lastq to \numquestions. Otherwise, -% \find@qrange makes sure the grading range is defined and that -% its last question isn't before its first question. -% \find@qrange then calls \@tblquestions, passing along the argument -% that is either ``v'' or ``h''. +% When we get to \find@qrange, we know we're doing a table indexed by +% question numbers and that this is not the first run of latex. The +% argument is either ``v'' or ``h''. If we're not doing a partial +% table, then \find@qrange sets \tbl@firstq and \first@pq@index to 1 +% and \tbl@lastq and \last@pq@index to \numquestions. Otherwise, +% \find@qrange makes sure the grading range is defined and that its +% last question isn't before its first question. \find@qrange then +% calls \tbl@v@or@h, passing along the argument that is either ``v'' +% or ``h''. \def\find@qrange#1{% + % We get here from \find@p@or@q@range. % We're doing a table indexed by question numbers. \if@partial \@ifundefined{range@\tbl@range @firstq}% @@ -4874,12 +5823,14 @@ {% \edef\tbl@firstq{\csname range@\tbl@range @firstq\endcsname}% \edef\tbl@lastq{\csname range@\tbl@range @lastq\endcsname}% + \let\first@pq@index=\tbl@firstq + \let\last@pq@index=\tbl@lastq % Check that firstq precedes or equals lastq: \ifnum \tbl@firstq > \tbl@lastq\relax - \fbox{Error: Grading Range `\tbl@range ': + \fbox{\textbf{Error:} Grading Range `\tbl@range': Last question precedes first question.}% \ClassError{exam}{% - In grading range `\tbl@range ', + In grading range `\tbl@range', the last question\MessageBreak \space\space comes before the first question.\MessageBreak }{% @@ -4888,463 +5839,51 @@ least one question.\MessageBreak }% \else - \@tblquestions{#1}% + \tbl@v@or@h{#1}% \fi }% }% \else \def\tbl@firstq{1}% + \let\first@pq@index=\tbl@firstq % \numquestions is always defined, even if this is the first % run of LaTeX and \exam@numquestions isn't defined. % If it's the first run of LaTeX, then its value isn't useful, % but it's never used until a later run (when its value is useful). \def\tbl@lastq{\numquestions}% - \@tblquestions{#1}% + \let\last@pq@index=\tbl@lastq + \tbl@v@or@h{#1}% \fi }% find@qrange -\def\@tblquestions#1{% - % \tbl@firstq and \tbl@lastq have already been set. - % The argument is either ``v'' or ``h', and we branch accordingly. - \if v#1% - \@vtblquestions +\def\@vref{v} +\def\@href{h} +\def\tbl@v@or@h#1{% + % \first@pq@index=\tbl@firstq or \tbl@firstp and + % \last@pq@index=\tbl@lastq or \tbl@lastp have already been set. + % The argument should be either `v' or `h', and we branch + % accordingly. + \def\exam@temp{#1}% + \ifx\exam@temp\@vref + \check@num@cols@v \else - \if h#1% - \@htblquestions + \ifx\exam@temp\@href + \check@num@rows@h \else \ClassError{exam}{% - The first optional argument to \protect\gradetable,\MessageBreak - \space\space \protect\bonusgradetable, - \protect\pointtable, or\MessageBreak - \space\space \protect\bonuspointtable - \space must be either `h' or `v'\MessageBreak + Grade or point table: the argument `#1'\MessageBreak + \space\space must be `v' or `h'. + \MessageBreak }{% - Grade tables and point tables can be either horizontal or - vertical;\MessageBreak + Grade tables and point tables can be either vertical or + horizontal;\MessageBreak \space\space no diagonals allowed.\MessageBreak }% - \if@scores - \if@bonus - \fbox{Error: bonusgradetable: Invalid first optional argument - `#1'.}% - \else - \fbox{Error: gradetable: Invalid first optional argument - `#1'.}% - \fi - \else - \if@bonus - \fbox{Error: bonuspointtable: Invalid first optional argument - `#1'.}% - \else - \fbox{Error: pointtable: Invalid first optional argument - `#1'.}% - \fi - \fi + \fbox{\textbf{Error:} grade or point table: Invalid argument + `#1' must be `v' or `h'.}% \fi \fi -}% @tblquestions - -%-------------------------------------------------------------------- -% Vertical, indexed by question numbers: - -\def\@vtblquestions{% - % The table is vertical and indexed by question numbers. - % The question range has already been determined. - % Branch according to whether it's bonus, non-bonus, or combined, - % and gradetable or pointtable: - \begingroup - % Save the current value of question in @iterator, so that - % we can restore it after doing the table: - \setcounter{@iterator}{\value{question}}% - \renewcommand\arraystretch{\@gtblstretch}% - \if@bonus - \if@scores - \@bvgrdtblquestions - \else - \@bvpttblquestions - \fi - \else - \if@combined - \if@scores - \@cvgrdtblquestions - \else - \@cvpttblquestions - \fi - \else - \if@scores - \@vgrdtblquestions - \else - \@vpttblquestions - \fi - \fi - \fi - % Restore the saved value of question: - \setcounter{question}{\value{@iterator}}% - \endgroup -}% @vtblquestions - -\def\@cvgrdtblquestions{% - % Vertical combined gradetable, indexed by questions - \set@hlfcntr{tbl@points}{0}% - \set@hlfcntr{tbl@bonuspoints}{0}% - \begin{tabular}{|c|c|c|c|} - \hline - {\@cvqword}& {\@cvpword}& {\@cvbpword}& {\@cvsword}\\ - \hline - \setcounter{question}{\tbl@firstq}% - \addtocounter{question}{-1}\do@cvloop - {\@cvtword}& \prt@tablepoints& - \prt@tablebonuspoints& - \hbox to \@cellwidth{\hfill}\\ - \hline - \end{tabular}% -} -\def\do@cvloop{% - % \do@cvloop is used by \@cvgrdtblquestions - \addtocounter{question}{1}% - \ref{question@\arabic{question}} & - \pointsofquestion{\arabic{question}}& - \bonuspointsofquestion{\arabic{question}}&\\ - \hline - \@ifundefined{pointsofq@\romannumeral \c@question}% - {}% - {\addto@hlfcntr{tbl@points} - {\csname pointsofq@\romannumeral \c@question\endcsname}}% - \@ifundefined{bonuspointsofq@\romannumeral \c@question}% - {}% - {\addto@hlfcntr{tbl@bonuspoints} - {\csname bonuspointsofq@\romannumeral \c@question\endcsname}}% - \ifnum \value{question} < \tbl@lastq\relax - \let\next@cvloop=\do@cvloop - \else - \let\next@cvloop=\relax - \fi - \next@cvloop -}% do@cvloop - - -\def\@cvpttblquestions{% - % Vertical combined pointtable, indexed by questions - \set@hlfcntr{tbl@points}{0}% - \set@hlfcntr{tbl@bonuspoints}{0}% - \begin{tabular}{|c|c|c|} - \hline - {\@cvqword}& {\@cvpword}& {\@cvbpword}\\ - \hline - \setcounter{question}{\tbl@firstq}% - \addtocounter{question}{-1}\do@cvptloop - {\@cvtword}& \prt@tablepoints& - \prt@tablebonuspoints\\ - \hline - \end{tabular}% -} -\def\do@cvptloop{% - % \do@cvptloop is used by \@cvpttblquestions - \addtocounter{question}{1}% - \ref{question@\arabic{question}} & - \pointsofquestion{\arabic{question}}& - \bonuspointsofquestion{\arabic{question}}\\ - \hline - \@ifundefined{pointsofq@\romannumeral \c@question}% - {}% - {\addto@hlfcntr{tbl@points} - {\csname pointsofq@\romannumeral \c@question\endcsname}}% - \@ifundefined{bonuspointsofq@\romannumeral \c@question}% - {}% - {\addto@hlfcntr{tbl@bonuspoints} - {\csname bonuspointsofq@\romannumeral \c@question\endcsname}}% - \ifnum \value{question} < \tbl@lastq\relax - \let\next@cvptloop=\do@cvptloop - \else - \let\next@cvptloop=\relax - \fi - \next@cvptloop -}% do@cvptloop - - -\def\@bvgrdtblquestions{% - % Vertical bonus gradetable, indexed by questions: - \set@hlfcntr{tbl@bonuspoints}{0}% - \begin{tabular}{|c|c|c|} - \hline - {\@bvqword}& {\@bvpword}& {\@bvsword}\\ - \hline - \setcounter{question}{\tbl@firstq}% - \addtocounter{question}{-1}\do@bvloop - {\@bvtword}& \prt@tablebonuspoints&\hbox to \@cellwidth{\hfill}\\ - \hline - \end{tabular}% -}% @bvgrdtblquestions -\def\do@bvloop{% - % \do@bvloop is used by \@bvgrdtblquestions - \addtocounter{question}{1}% - \ref{question@\arabic{question}} & - \bonuspointsofquestion{\arabic{question}}&\\ - \hline - \@ifundefined{bonuspointsofq@\romannumeral \c@question}% - {}% - {\addto@hlfcntr{tbl@bonuspoints} - {\csname bonuspointsofq@\romannumeral \c@question\endcsname}}% - \ifnum \value{question} < \tbl@lastq\relax - \let\next@bvloop=\do@bvloop - \else - \let\next@bvloop=\relax - \fi - \next@bvloop -}% do@bvloop - - -\def\@bvpttblquestions{% - % Vertical bonus point table, indexed by questions: - \set@hlfcntr{tbl@bonuspoints}{0}% - \begin{tabular}{|c|c|} - \hline - {\@bvqword}& {\@bvpword}\\ - \hline - \setcounter{question}{\tbl@firstq}% - \addtocounter{question}{-1}\do@bvptloop - {\@bvtword}& \prt@tablebonuspoints\\ - \hline - \end{tabular}% -}% @bvpttblquestions -\def\do@bvptloop{% - % \do@bvptloop is used by \@bvpttblquestions - \addtocounter{question}{1}% - \ref{question@\arabic{question}} & - \bonuspointsofquestion{\arabic{question}}\\ - \hline - \@ifundefined{bonuspointsofq@\romannumeral \c@question}% - {}% - {\addto@hlfcntr{tbl@bonuspoints} - {\csname bonuspointsofq@\romannumeral \c@question\endcsname}}% - \ifnum \value{question} < \tbl@lastq\relax - \let\next@bvptloop=\do@bvptloop - \else - \let\next@bvptloop=\relax - \fi - \next@bvptloop -}% do@bvptloop - -\def\@vgrdtblquestions{% - % Vertical non-bonus grade table, indexed by questions: - \set@hlfcntr{tbl@points}{0}% - \begin{tabular}{|c|c|c|} - \hline - {\@vqword}& {\@vpword}& {\@vsword}\\ - \hline - \setcounter{question}{\tbl@firstq}% - \addtocounter{question}{-1}\do@vloop - {\@vtword}& \prt@tablepoints&\hbox to \@cellwidth{\hfill}\\ - \hline - \end{tabular}% -}% @vgrdtblquestions -\def\do@vloop{% - % \do@vloop is used by \@vgrdtblquestions - \addtocounter{question}{1}% - \ref{question@\arabic{question}} & - \pointsofquestion{\arabic{question}}&\\ - \hline - \@ifundefined{pointsofq@\romannumeral \c@question}% - {}% - {\addto@hlfcntr{tbl@points} - {\csname pointsofq@\romannumeral \c@question\endcsname}}% - \ifnum \value{question} < \tbl@lastq\relax - \let\next@vloop=\do@vloop - \else - \let\next@vloop=\relax - \fi - \next@vloop -}% do@vloop - - -\def\@vpttblquestions{% - % Vertical non-bonus point table, indexed by questions: - \set@hlfcntr{tbl@points}{0}% - \begin{tabular}{|c|c|} - \hline - {\@vqword}& {\@vpword}\\ - \hline - \setcounter{question}{\tbl@firstq}% - \addtocounter{question}{-1}\do@vptloop - {\@vtword}& \prt@tablepoints\\ - \hline - \end{tabular}% -}% @vpttblquestions -\def\do@vptloop{% - % \do@vptloop is used by \@vpttblquestions - \addtocounter{question}{1}% - \ref{question@\arabic{question}} & - \pointsofquestion{\arabic{question}}\\ - \hline - \@ifundefined{pointsofq@\romannumeral \c@question}% - {}% - {\addto@hlfcntr{tbl@points} - {\csname pointsofq@\romannumeral \c@question\endcsname}}% - \ifnum \value{question} < \tbl@lastq\relax - \let\next@vptloop=\do@vptloop - \else - \let\next@vptloop=\relax - \fi - \next@vptloop -}% do@vptloop - - -%-------------------------------------------------------------------- -% Horizontal, indexed by question numbers: - -\def\@htblquestions{% - % We get here from \@tblquestions. - % The table is horizontal and indexed by question numbers. - % The question range has already been determined. - % Set num@cols equal to the number of questions on the table, and - % do either bonus, combined, or non-bonus table, in each case - % putting in a line for scores only if it's a gradetable: - \@ifundefined{exam@numquestions}% - {}% - {% - \setcounter{num@cols}{\tbl@lastq}% - \addtocounter{num@cols}{-\tbl@firstq}% - \addtocounter{num@cols}{1}% - }% - % Do either bonus, combined, or non-bonus table. In each case, put - % in a line for scores if it's a gradetable: - \begingroup - % Save the current value of question in @iterator, so that - % we can restore it after doing the table: - \setcounter{@iterator}{\value{question}}% - \renewcommand\arraystretch{\@gtblstretch}% - \if@bonus - % It's a horizontal bonus table, by questions: - \begin{tabular}{|l|*{\thenum@cols}{c|}c|} - \hline - {\@bhqword}& \setcounter{question}{\tbl@firstq}% - \addtocounter{question}{-1}\do@qnumloop - {\@bhtword}\\ - \hline - \set@hlfcntr{tbl@bonuspoints}{0}% - {\@bhpword}& \setcounter{question}{\tbl@firstq}% - \addtocounter{question}{-1}\do@bptloop - \prt@tablebonuspoints\\ - \hline - % If it's a grade table, add in the score line: - \if@scores - {\@bhsword}& \setcounter{question}{\tbl@firstq}% - \addtocounter{question}{-1}\do@sloop - \\ - \hline - \fi - \end{tabular}% - \else - \if@combined - % It's a horizontal combined table, by questions: - \begin{tabular}{|l|*{\thenum@cols}{c|}c|} - \hline - {\@chqword}& \setcounter{question}{\tbl@firstq}% - \addtocounter{question}{-1}\do@qnumloop - {\@chtword}\\ - \hline - \set@hlfcntr{tbl@points}{0}% - {\@chpword}& \setcounter{question}{\tbl@firstq}% - \addtocounter{question}{-1}\do@ptloop - \prt@tablepoints\\ - \hline - \set@hlfcntr{tbl@bonuspoints}{0}% - {\@chbpword}& \setcounter{question}{\tbl@firstq}% - \addtocounter{question}{-1}\do@bptloop - \prt@tablebonuspoints\\ - \hline - % If it's a grade table, add in the score line: - \if@scores - {\@chsword}& \setcounter{question}{\tbl@firstq}% - \addtocounter{question}{-1}\do@sloop - \\ - \hline - \fi - \end{tabular}% - \else - % Horizontal non-bonus table, indexed by question number: - \set@hlfcntr{tbl@points}{0}% - \begin{tabular}{|l|*{\thenum@cols}{c|}c|} - \hline - {\@hqword}& \setcounter{question}{\tbl@firstq}% - \addtocounter{question}{-1}\do@qnumloop - {\@htword}\\ - \hline - {\@hpword}& \setcounter{question}{\tbl@firstq}% - \addtocounter{question}{-1}\do@ptloop - \prt@tablepoints\\ - \hline - % If it's a grade table, add in the score line: - \if@scores - {\@hsword}& \setcounter{question}{\tbl@firstq}% - \addtocounter{question}{-1}\do@sloop - \\ - \hline - \fi - \end{tabular}% - \fi - \fi - % Restore the saved value of question: - \setcounter{question}{\value{@iterator}}% - \endgroup -}% @htblquestions - - -% \do@qnumloop and \do@sloop are used by non-bonus, -% combined, and bonus tables: -\def\do@qnumloop{% - \addtocounter{question}{1}% - \ref{question@\arabic{question}}& - \ifnum \value{question} < \tbl@lastq\relax - \let\next@qnloop=\do@qnumloop - \else - \let\next@qnloop=\relax - \fi - \next@qnloop -}% do@qnumloop -\def\do@sloop{% - \addtocounter{question}{1}% - \hbox to \@cellwidth{\hfill}& - \ifnum \value{question} < \tbl@lastq\relax - \let\next@sloop=\do@sloop - \else - \let\next@sloop=\relax - \fi - \next@sloop -}% do@sloop - -% \do@ptloop is used by combined and non-bonus tables: -\def\do@ptloop{% - \addtocounter{question}{1}% - \pointsofquestion{\arabic{question}}& - \@ifundefined{pointsofq@\romannumeral \c@question}% - {}% - {\addto@hlfcntr{tbl@points} - {\csname pointsofq@\romannumeral \c@question\endcsname}}% - \ifnum \value{question} < \tbl@lastq\relax - \let\next@ptloop=\do@ptloop - \else - \let\next@ptloop=\relax - \fi - \next@ptloop -}% do@ptloop - -% \do@bptloop is used by bonus and combined tables: -\def\do@bptloop{% - \addtocounter{question}{1}% - \bonuspointsofquestion{\arabic{question}}& - \@ifundefined{bonuspointsofq@\romannumeral \c@question}% - {}% - {\addto@hlfcntr{tbl@bonuspoints} - {\csname bonuspointsofq@\romannumeral \c@question\endcsname}}% - \ifnum \value{question} < \tbl@lastq\relax - \let\next@bptloop=\do@bptloop - \else - \let\next@bptloop=\relax - \fi - \next@bptloop -}% do@bptloop - +}% tbl@v@or@h %-------------------------------------------------------------------- %-------------------------------------------------------------------- @@ -5374,18 +5913,23 @@ % \find@prange makes sure the grading range is defined and that its % last page isn't before its first page (if it's a partial table). In % any case, it then sets \tbl@firstp and \tbl@lastp, and calls -% \@tblpages. +% \check@secondrun. \def\find@prange#1{% + % We get here from \find@p@or@q@range. % We're doing a table indexed by pages. % The argument is either ``v'' or ``h''. - % After determining the first and last page of the range, we - % call \@tblpages, passing it that argument (i.e., we say - % \@tblpages{#1}). - % If not a partial table, we set \tbl@firstp to 1 and \tbl@lastp to + % We first determine the first and last page of the range, storing + % those in \first@pq@index=\tbl@firstp and + % \last@pq@index=\tbl@lastp. If not a partial table, we set + % \first@pq@index=\tbl@firstp to 1 and \last@pq@index=\tbl@lastp to % the last page with the appropriate points (and so if it's a % combined table, it's the last page to have either bonus or % non-bonus points). + % We then call \check@secondrun, passing it the argument that we + % received (i.e., we say \check@secondrun{#1}) to make sure + % we've done at least two runs of latex (so that we'll have the + % information we need about which pages have points on them). \if@partial \@ifundefined{range@\tbl@range @firstp}% {% @@ -5399,811 +5943,1524 @@ {% \edef\tbl@firstp{\csname range@\tbl@range @firstp\endcsname}% \edef\tbl@lastp{\csname range@\tbl@range @lastp\endcsname}% + \let\first@pq@index=\tbl@firstp + \let\last@pq@index=\tbl@lastp % Check that firstp precedes or equals lastp: \ifnum \tbl@firstp > \tbl@lastp\relax - \fbox{Error: Grading Range `\tbl@range ': + \fbox{\textbf{Error:} Grading Range `\tbl@range ': Last page precedes first page.}% \ClassError{exam}{% - In grading range `\tbl@range ', the last page\MessageBreak + In grading range `\tbl@range', the last page\MessageBreak \space\space comes before the first page.\MessageBreak }{% \string\begingradingrange \space must precede \string\endgradingrange.\MessageBreak }% \else - \@tblpages{#1}% + \check@secondrun{#1}% \fi }% }% \else + % It's not a partial table: \def\tbl@firstp{1}% - % \tbl@lastp isn't used on the first run of LaTeX, and + \let\first@pq@index=\tbl@firstp + % We never get here on the first run of LaTeX, and % \lastpage@withbonuspoints is defined on the second and later runs. \def\tbl@lastp{\lastpage@withpoints}% + \let\last@pq@index=\tbl@lastp \if@bonus \def\tbl@lastp{\lastpage@withbonuspoints}% + \let\last@pq@index=\tbl@lastp \fi \if@combined \ifnum \lastpage@withbonuspoints > \lastpage@withpoints\relax \def\tbl@lastp{\lastpage@withbonuspoints}% + \let\last@pq@index=\tbl@lastp \fi \fi - \@tblpages{#1}% + \check@secondrun{#1}% \fi }% find@prange - -\def\@tblpages#1{% +\def\check@secondrun#1{% + % The function \ii@gtable already made sure that this isn't the + % first run of latex. To do a table indexed by pages, though, we + % have to also make sure it's not the second run of latex. % We get here from \find@prange; the argument is either ``v'' or % ``h''. % Check that there's enough info from the .aux file to do a page - % indexed grade table. If so, call \@whichtblpgs{#1}: - \@ifundefined{lastpage@withpoints}% - {\ClassWarning{exam}{% - You must run LaTeX twice more\MessageBreak - \space\space to produce the table.\MessageBreak}% - \fbox{Run \LaTeX{} twice more to produce the table}% - }% - {% - \@ifundefined{pointsonpage@\romannumeral + % indexed grade table. If so, call \tbl@v@or@h{#1}: + \@ifundefined{pointsonpage@\romannumeral \csname lastpage@withpoints\endcsname}% + {\@ifundefined{bonuspointsonpage@\romannumeral + \csname lastpage@withbonuspoints\endcsname}% {\ClassWarning{exam}{% - You must run LaTeX again\MessageBreak - \space\space to produce the table.\MessageBreak}% + You must run LaTeX again to produce the table.\MessageBreak}% \fbox{Run \LaTeX{} again to produce the table}% }% - {% - \@whchtblpgs#1 + {\tbl@v@or@h{#1}% }% }% -}% @tblpages - -\def\@whchtblpgs#1{% - % At this point, we know the table is indexed by pages. - % It can be vertical or horizontal, - % grade or point, and bonus or non-bonus. - % The argument is either ``v'' or ``h''; test it, and branch: - \if v#1% - \@vtblpages - \else - \if h#1% - \@htblpages - \else - \ClassError{exam}{% - The first optional argument to - \protect\gradetable,\MessageBreak - \space\space \protect\bonusgradetable, - \protect\pointtable, or\MessageBreak - \space\space - \protect\bonuspointtable \space must be either `h' or - `v'\MessageBreak - }{% - Grade tables and point tables can be either horizontal or - vertical;\MessageBreak - \space\space no diagonals allowed.\MessageBreak - }% - \if@scores - \if@bonus - \fbox{Error: bonusgradetable: Invalid first optional argument - `#1'.}% - \else - \fbox{Error: gradetable: Invalid first optional argument - `#1'.}% - \fi - \else - \if@bonus - \fbox{Error: bonuspointtable: Invalid first optional argument - `#1'.}% - \else - \fbox{Error: pointtable: Invalid first optional argument - `#1'.}% - \fi - \fi - \fi - \fi -}% \@whchtblpgs + {\tbl@v@or@h{#1}% + }% +}% check@secondrun %-------------------------------------------------------------------- -% Vertical, indexed by pages: - -\def\@vtblpages{% - % We get here from \@whchtblpgs. - % At this point, we know it's a vertical table indexed by pages. - % It can be grade or point, and bonus, non-bonus, or combined. - \begingroup - \renewcommand\arraystretch{\@gtblstretch}% - \if@bonus - % It's vertical, bonus, by pages, either point or grade: - \set@hlfcntr{tbl@bonuspoints}{0}% - \if@scores - \begin{tabular}{|c|c|c|} - \hline - {\@bvpgword}& {\@bvpword}& {\@bvsword}\\ - \hline - \setcounter{@iterator}{\tbl@firstp}% - \addtocounter{@iterator}{-1}\pg@bvloop - {\@bvtword}& \prt@tablebonuspoints& - \hbox to \@cellwidth{\hfill}\\ - \hline - \end{tabular}% - \else - \begin{tabular}{|c|c|} - \hline - {\@bvpgword}& {\@bvpword}\\ - \hline - \setcounter{@iterator}{\tbl@firstp}% - \addtocounter{@iterator}{-1}\pg@ptbvloop - {\@bvtword}& \prt@tablebonuspoints\\ - \hline - \end{tabular}% - \fi +% Indexed by pages: + +% For a table indexed by pages, we need to know how many pages there +% are with points on them. The argument to \count@pgswpts should be +% the name of a counter; we set that counter equal to the number of +% pages with the appropriate kind of points. + +\def\count@pgswpts#1{% + % Set the counter #1 equal to the number of pages in the range with + % the appropriate type of points. + % We're called by \@computenumcols@h and \@computenumrows@v. + \setcounter{#1}{0}% + \setcounter{@iterator}{\tbl@firstp}% + \addtocounter{@iterator}{-1}% + \if@bonus + \docount@pgswbpts{#1}% + \else + \if@combined + \docount@pgswcpts{#1}% \else - \if@combined - % It's vertical, combined, by pages, either point or grade: - \set@hlfcntr{tbl@points}{0}% - \set@hlfcntr{tbl@bonuspoints}{0}% - \if@scores - % Combined gradetable, vertical, by pages: - \begin{tabular}{|c|c|c|c|} - \hline - {\@cvpgword}& {\@cvpword}& {\@cvbpword}& {\@cvsword}\\ - \hline - \setcounter{@iterator}{\tbl@firstp}% - \addtocounter{@iterator}{-1}\pg@cvloop - {\@cvtword}& \prt@tablepoints& - \prt@tablebonuspoints& - \hbox to \@cellwidth{\hfill}\\ - \hline - \end{tabular}% - \else - % Combined pointtable, vertical, by pages: - \begin{tabular}{|c|c|c|} - \hline - {\@cvpgword}& {\@cvpword}& {\@cvbpword}\\ - \hline - \setcounter{@iterator}{\tbl@firstp}% - \addtocounter{@iterator}{-1}\pg@ptcvloop - {\@cvtword}& \prt@tablepoints& - \prt@tablebonuspoints\\ - \hline - \end{tabular}% - \fi - \else - % It's non-bonus, either point or grade: - \set@hlfcntr{tbl@points}{0}% - \if@scores - \begin{tabular}{|c|c|c|} - \hline - {\@vpgword}& {\@vpword}& {\@vsword}\\ - \hline - \setcounter{@iterator}{\tbl@firstp}% - \addtocounter{@iterator}{-1}\pg@vloop - {\@vtword}& \prt@tablepoints&\hbox to \@cellwidth{\hfill}\\ - \hline - \end{tabular}% - \else - \begin{tabular}{|c|c|} - \hline - {\@vpgword}& {\@vpword}\\ - \hline - \setcounter{@iterator}{\tbl@firstp}% - \addtocounter{@iterator}{-1}\pg@ptvloop - {\@vtword}& \prt@tablepoints\\ - \hline - \end{tabular}% - \fi - \fi + \docount@pgswpts{#1}% \fi - \endgroup -}% @vtblpages - + \fi +}% count@pgswpts -% \pg@ptvloop and \pg@ptvloopline are used only by non-bonus pointtable: -\def\pg@ptvloop{% +\def\docount@pgswcpts#1{% + % Called by \count@pgswpts + % Count the number of pages in range with any kind of point (bonus + % or non-bonus): \addtocounter{@iterator}{1}% \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}% \ifhlfcntr@pos{tmp@hlfcntr}% - \add@hlfcntrtohlfcntr{tbl@points}{tmp@hlfcntr}% - \pg@ptvloopline + \addtocounter{#1}{1}% + \else + \check@bnsptpage{#1}% \fi \ifnum \the@iterator < \tbl@lastp\relax - \let\next@pg@ptvloop=\pg@ptvloop + \def\nextdocount@pgswcpts{\docount@pgswcpts{#1}}% \else - \let\next@pg@ptvloop=\relax + \let\nextdocount@pgswcpts=\relax \fi - \next@pg@ptvloop -}% pg@ptvloop -\def\pg@ptvloopline{% - % We still don't understand why we need to hide this inside of a - % macro; there's some weird interaction with the \ifnum checking to - % see if something is ``>0''; checking that something is ``=0'' - % doesn't cause the ``\ifnum incomplete'' (or whatever) error. - \pageref{firstpoints@onpage@\arabic{@iterator}} & - \pointsonpage{\the@iterator}\\ - \hline -}% pg@ptvloopline + \nextdocount@pgswcpts +}% docount@pgswcpts +\def\check@bnsptpage#1{% + % We need to hide this inside of a macro because if \ifhlfcntr@pos + % isn't expanded (because this stuff is being skipped in the outer + % conditional), then TeX doesn't see the \ifnum hidden inside the + % \ifhlfcntr@pos, but it does see the \fi, and so it get confused. + \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}% + \ifhlfcntr@pos{tmp@hlfcntr}% + \addtocounter{#1}{1}% + \fi +}% check@bnsptpage -% \pg@vloop and \pg@vloopline are used only by non-bonus gradetable: -\def\pg@vloop{% +\def\docount@pgswpts#1{% + % Called by \count@pgswpts. + % Count the number of pages in range with regular points. \addtocounter{@iterator}{1}% \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}% \ifhlfcntr@pos{tmp@hlfcntr}% - \add@hlfcntrtohlfcntr{tbl@points}{tmp@hlfcntr}% - \pg@vloopline + \addtocounter{#1}{1}% \fi \ifnum \the@iterator < \tbl@lastp\relax - \let\next@pg@vloop=\pg@vloop + \def\nextdocount@pgswpts{\docount@pgswpts{#1}}% \else - \let\next@pg@vloop=\relax + \let\nextdocount@pgswpts=\relax \fi - \next@pg@vloop -}% pg@vloop -\def\pg@vloopline{% - % We still don't understand why we need to hide this inside of a - % macro; there's some weird interaction with the \ifnum checking to - % see if something is ``>0''; checking that something is ``=0'' - % doesn't cause the ``\ifnum incomplete'' (or whatever) error. - \pageref{firstpoints@onpage@\arabic{@iterator}} & - \pointsonpage{\the@iterator}&\\ - \hline -}% pg@vloopline + \nextdocount@pgswpts +}% docount@pgswpts -% \pg@cvloop, \pg@noncvloopline, \check@bnspts, and -% \pg@cvloopline are used only by combined gradetable: -\def\pg@cvloop{% +\def\docount@pgswbpts#1{% + % Called by \count@pgswpts + % Count the number of pages in range with bonus points. \addtocounter{@iterator}{1}% - % Add the bonus points: \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}% - \add@hlfcntrtohlfcntr{tbl@bonuspoints}{tmp@hlfcntr}% - % Add the regular points: - \set@hlfcntr{tmp@hlfcntr}{\csname pointsonpage@\romannumeral - \csname c@@iterator\endcsname\endcsname}% - \add@hlfcntrtohlfcntr{tbl@points}{tmp@hlfcntr}% - % Print a line of the table, if there are points on this page: \ifhlfcntr@pos{tmp@hlfcntr}% - \pg@noncvloopline - \else - \check@bnspts + \addtocounter{#1}{1}% \fi - % Do the tail recursion bit: \ifnum \the@iterator < \tbl@lastp\relax - \let\next@pg@cvloop=\pg@cvloop + \def\nextdocount@pgswbpts{\docount@pgswbpts{#1}}% \else - \let\next@pg@cvloop=\relax + \let\nextdocount@pgswbpts=\relax \fi - \next@pg@cvloop -}% pg@cvloop -\def\pg@noncvloopline{% - % We still don't understand why we need to hide this inside of a - % macro; there's some weird interaction with the \ifnum checking to - % see if something is ``>0''; checking that something is ``=0'' - % doesn't cause the ``\ifnum incomplete'' (or whatever) error. - \pageref{firstpoints@onpage@\arabic{@iterator}}& - \pointsonpage{\the@iterator}& - \bonuspointsonpage{\the@iterator}&\\ - \hline -}% pg@noncvloopline -\def\check@bnspts{% - % We still don't understand why we need to hide this inside of a - % macro; there's some weird interaction with the \ifnum checking to - % see if something is ``>0''; checking that something is ``=0'' - % doesn't cause the ``\ifnum incomplete'' (or whatever) error. - \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \pg@cvloopline - \fi -}% check@bnspts -\def\pg@cvloopline{% - % We still don't understand why we need to hide this inside of a - % macro; there's some weird interaction with the \ifnum checking to - % see if something is ``>0''; checking that something is ``=0'' - % doesn't cause the ``\ifnum incomplete'' (or whatever) error. - \pageref{firstbonuspoints@onpage@\arabic{@iterator}}& - \pointsonpage{\the@iterator}& - \bonuspointsonpage{\the@iterator}&\\ - \hline -}% pg@cvloopline + \nextdocount@pgswbpts +}% docount@pgswbpts -% \pg@ptcvloop, \pg@ptnoncvloopline, \check@ptbnspts, and -% \pg@ptcvloopline are used only by combined pointtable: -\def\pg@ptcvloop{% - \addtocounter{@iterator}{1}% - % Add the bonus points: - \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}% - \add@hlfcntrtohlfcntr{tbl@bonuspoints}{tmp@hlfcntr}% - % Add the regular points: - \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}% - \add@hlfcntrtohlfcntr{tbl@points}{tmp@hlfcntr}% - % Print a line of the table, if there are points on this page: - \ifhlfcntr@pos{tmp@hlfcntr}% - \pg@ptnoncvloopline +%-------------------------------------------------------------------- +%-------------------------------------------------------------------- +% Multirow horizontal tables, indexed by question numbers: + +\newcounter{pq@index}% In tables indexed by page numbers, it holds a +% page number. In tables indexed by question numbers, it holds a +% question number. + +\newcounter{pq@index@pts}% In horizontal tables, this holds either the +% current page number or the current question number as we put the +% point values for that page or question number into the table. In +% vertical tables, this holds the index for the first column of the +% current row. + +\newcounter{pq@index@bpts}% used to set bonus point values in +% horizontal tables. Often used as scratch elsewhere. + +\def\hidden@ampersand{&}% Needed because an ampersand can't appear in +% the replacement text of a conditional. + +\newif\iftbl@pgs +% \tbl@pgstrue means a table indexed by page numbers +% \tbl@pgsfalse means a table indexed by question numbers + +\newcounter{num@cols} +\newcounter{num@rows} +\newcounter{current@row} +\newcounter{cols@done}% Holds the number of columns done in the +% current row. + +%-------------------------------------------------------------------- +%-------------------------------------------------------------------- +% Stuff to unify tables indexed by questions and tables indexed by +% pages: + +% \first@pq@index and \last@pq@index will hold either \tbl@firstq and +% \tbl@lastq or \tbl@firstp and \tbl@lastp. + +\def\increment@index#1{% + % If we're doing a table indexed by question numbers, we increment + % the counter #1. + % If we're doing a table indexed by page numbers, + % we increase the counter #1 by at least 1 to either the number of the + % next page containing the appropriate kind of points, or to + % something greater than \tbl@lastp. + \iftbl@pgs + \find@nextpagewithpoints{#1}% \else - \check@ptbnspts + \addtocounter{#1}{1}% \fi - % Do the tail recursion bit: - \ifnum \the@iterator < \tbl@lastp\relax - \let\next@pg@ptcvloop=\pg@ptcvloop +}% increment@index + +\def\nextcolumn@index@v#1{% + % Used only for multicolumn tables. + % If we're doing a table indexed by question numbers, we increase + % the counter #1 by num@cols. + % If we're doing a table indexed by page numbers, + % we use \find@nextcolumnpage@v to increment the counter #1 to either + % the (num@rows)'th page number after #1 that contains the + % appropriate kind of points or to a value greater than \tbl@lastp. + \iftbl@pgs + \find@nextcolumnpage@v{#1}% \else - \let\next@pg@ptcvloop=\relax + \addtocounter{#1}{\value{num@rows}}% \fi - \next@pg@ptcvloop -}% pg@ptcvloop -\def\pg@ptnoncvloopline{% - % We still don't understand why we need to hide this inside of a - % macro; there's some weird interaction with the \ifnum checking to - % see if something is ``>0''; checking that something is ``=0'' - % doesn't cause the ``\ifnum incomplete'' (or whatever) error. - \pageref{firstpoints@onpage@\arabic{@iterator}}& - \pointsonpage{\the@iterator}& - \bonuspointsonpage{\the@iterator}\\ - \hline -}% pg@ptnoncvloopline -\def\check@ptbnspts{% - % We still don't understand why we need to hide this inside of a - % macro; there's some weird interaction with the \ifnum checking to - % see if something is ``>0''; checking that something is ``=0'' - % doesn't cause the ``\ifnum incomplete'' (or whatever) error. - \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \pg@ptcvloopline +}% nextcolumn@index@v + +\def\pointsof@index#1{% + \iftbl@pgs + \pointsonpage{\arabic{#1}}% + \else + \pointsofquestion{\arabic{#1}}% \fi -}% check@ptbnspts -\def\pg@ptcvloopline{% - % We still don't understand why we need to hide this inside of a - % macro; there's some weird interaction with the \ifnum checking to - % see if something is ``>0''; checking that something is ``=0'' - % doesn't cause the ``\ifnum incomplete'' (or whatever) error. - \pageref{firstbonuspoints@onpage@\arabic{@iterator}}& - \pointsonpage{\the@iterator}& - \bonuspointsonpage{\the@iterator}\\ - \hline -}% pg@ptcvloopline +}% pointsof@index -% \pg@bvloop and \pg@bvloopline are used only by bonus gradetable: -\def\pg@bvloop{% - \addtocounter{@iterator}{1}% - \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \add@hlfcntrtohlfcntr{tbl@bonuspoints}{tmp@hlfcntr}% - \pg@bvloopline +\def\bonuspointsof@index#1{% + \iftbl@pgs + \bonuspointsonpage{\arabic{#1}}% + \else + \bonuspointsofquestion{\arabic{#1}}% \fi - \ifnum \the@iterator < \tbl@lastp\relax - \let\next@pg@bvloop=\pg@bvloop +}% bonuspointsof@index + +\def\refto@index#1{% + \iftbl@pgs + \if@combined + % Need to hide this inside of a macro: + \refto@comb@index{#1}% + \else + \if@bonus + \pageref{firstbonuspoints@onpage@\arabic{#1}}% + \else + \pageref{firstpoints@onpage@\arabic{#1}}% + \fi + \fi \else - \let\next@pg@bvloop=\relax + \ref{question@\arabic{#1}}% \fi - \next@pg@bvloop -}% pg@bvloop -\def\pg@bvloopline{% - % We still don't understand why we need to hide this inside of a - % macro; there's some weird interaction with the \ifnum checking to - % see if something is ``>0''; checking that something is ``=0'' - % doesn't cause the ``\ifnum incomplete'' (or whatever) error. - \pageref{firstbonuspoints@onpage@\arabic{@iterator}} & - \bonuspointsonpage{\the@iterator}&\\ - \hline -}% pg@bvloopline +}% refto@index -% \pg@ptbvloop and \pg@ptbvloopline are used only by bonus pointtable: -\def\pg@ptbvloop{% - \addtocounter{@iterator}{1}% - \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}% +\def\refto@comb@index#1{% + % We're called only by \refto@index. + % We can't have the \ifhlfcntr@pos...\fi inside of another + % conditional, so we're hiding it in this macro. + \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\arabic{#1}}}% \ifhlfcntr@pos{tmp@hlfcntr}% - \add@hlfcntrtohlfcntr{tbl@bonuspoints}{tmp@hlfcntr}% - \pg@ptbvloopline + \pageref{firstpoints@onpage@\arabic{#1}}% + \else + % In theory, there *must* be bonus points on this page, because + % there aren't any plain points, but there are allegedly *some* + % points. We're being brave and assuming that's correct, and not + % checking (which we'd have to hide inside a macro, because it + % would use \ifhlfcntr@pos): + \pageref{firstbonuspoints@onpage@\arabic{#1}}% \fi - \ifnum \the@iterator < \tbl@lastp\relax - \let\next@pg@ptbvloop=\pg@ptbvloop +}% refto@comb@index + +%-------------------------------------------------------------------- +%-------------------------------------------------------------------- +% Multirow tables: + + +%-------------------------------------------------------------------- +% Check that the number of rows is OK, and compute the number of +% columns: + +\def\check@num@rows@h{% + % We get here from \tbl@v@or@h. + % We make sure the number of rows is a positive integer. If it + % is, we go on to \@computenumcols@h + \ifnum \value{num@rows} < 1\relax + \ClassError{exam}{% + The number of rows in a table must be positive.\MessageBreak + }{% + The number of rows must be a positive integer.\MessageBreak + }% + \fbox{\textbf{Error:} Multirow table with no rows!}% \else - \let\next@pg@ptbvloop=\relax + \@computenumcols@h \fi - \next@pg@ptbvloop -}% pg@ptbvloop -\def\pg@ptbvloopline{% - % We still don't understand why we need to hide this inside of a - % macro; there's some weird interaction with the \ifnum checking to - % see if something is ``>0''; checking that something is ``=0'' - % doesn't cause the ``\ifnum incomplete'' (or whatever) error. - \pageref{firstbonuspoints@onpage@\arabic{@iterator}} & - \bonuspointsonpage{\the@iterator}\\ - \hline -}% pg@ptbvloopline +}% check@num@rows@h + +\def\@computenumcols@h{% + % We get here from \check@num@rows@h. + % Compute the number of columns. + % First: set num@cols to one more than either (the number of pages + % with the appropriate type of points) or (the number of questions), + % to have slots for the total along with the questions: + \iftbl@pgs + \count@pgswpts{num@cols}% + \addtocounter{num@cols}{1}% + \else + \setcounter{num@cols}{\tbl@lastq}% + \addtocounter{num@cols}{-\tbl@firstq}% + \addtocounter{num@cols}{2}% + \fi + % Save the number of slots needed in pq@index (used for scratch), to + % check for truncation: + \setcounter{pq@index}{\value{num@cols}}% + % Divide the number of slots needed by num@rows: + \divide \csname c@num@cols\endcsname by + \csname c@num@rows\endcsname + % Division truncates: See if there was truncation. + % Use @iterator as a scratch counter. + \setcounter{@iterator}{\value{num@cols}}% + \multiply \csname c@@iterator\endcsname by + \csname c@num@rows\endcsname + \ifnum \value{@iterator} < \value{pq@index}\relax + % There was truncation; add a column to num@cols: + \addtocounter{num@cols}{1}% + \fi + \@multirowtable +}% @computenumcols@h %-------------------------------------------------------------------- -% Horizontal, indexed by pages: +% Construct the actual table: -% For a horizontal table, before we begin we need to know how many -% pages there are with points on them: -\newcounter{num@cols} -\def\count@pgswpts{% - % We're called by \@htblpages. - % \count@pgswpts is used for horizontal - % grade tables and point tables, - % bonus, non-bonus, and combined. - \setcounter{num@cols}{0}% - \setcounter{@iterator}{\tbl@firstp}% - \addtocounter{@iterator}{-1}% +\def\@multirowtable{% + % We get here from \@computenumcols@h. + % All multirow tables! + \renewcommand\arraystretch{\@gtblstretch}% + \set@hlfcntr{tbl@points}{0}% + \set@hlfcntr{tbl@bonuspoints}{0}% + \setcounter{pq@index}{\first@pq@index}% + \addtocounter{pq@index}{-1}% + \setcounter{pq@index@pts}{\value{pq@index}}% + \setcounter{pq@index@bpts}{\value{pq@index}}% + \setcounter{current@row}{0}% + \begin{tabular}{|l|*{\value{num@cols}}{c|}} + \hline + \if@combined + \do@comblines@h + \else + \do@lines@h + \fi +}% @multirowtable + + +\def\do@lines@h{% + % Called only by \@multirowtable. + % It's either bonus or regular, but not combined: + \addtocounter{current@row}{1}% Set to the number of the current row + \iftbl@pgs + \if@bonus + \@bhpgword + \else + \@hpgword + \fi + \else + \if@bonus + \@bhqword + \else + \@hqword + \fi + \fi + \setcounter{cols@done}{0}% + \do@pq@indexloop@h + % When we finish \do@pq@indexloop@h, either we've finished a + % complete row of page numbers (or questions), or we've done all + % the page numbers (or questions) through \last@pq@index, or both: + \ifnum \value{cols@done} < \value{num@cols}\relax + % We've inserted all the page or question numbers, and there's + % room remaining on the current line for \@htword (or \@bhtword): + \ifnum \value{current@row} = \value{num@rows}\relax + % This is the last row; put in the total: + \do@htword@h + \else + % This isn't the last row. We insert (\value{num@cols} - + % \value{cols@done}) ampersands. + \setcounter{@iterator}{\value{num@cols}}% + \addtocounter{@iterator}{-\value{cols@done}}% + \do@emptycols@h + \fi + \fi + \\ + \hline + % Point values go here! + \setcounter{cols@done}{0}% \if@bonus - \docount@pgswbpts + \@bhpword + \do@bptloop@h \else - \if@combined - \docount@pgswcpts + \@hpword + \do@ptloop@h + \fi + % When we finish \do@ptloop@h or \do@bptloop@h, either + % we've finished a complete row of point values, or we've done all + % the question (or page) numbers through \last@pq@index, or both: + \ifnum \value{cols@done} < \value{num@cols}\relax + % We've inserted all the point values, and there's room + % remaining on the current line for Total Points: + \ifnum \value{current@row} = \value{num@rows}\relax + % This is the last row; put in the total: + \if@bonus + \do@totalbpts@h + \else + \do@totalpts@h + \fi \else - \docount@pgswpts + % This isn't the last row. We insert (\value{num@cols} - + % \value{cols@done}) ampersands. + \setcounter{@iterator}{\value{num@cols}}% + \addtocounter{@iterator}{-\value{cols@done}}% + \do@emptycols@h \fi \fi -}% count@pgswpts - -\def\docount@pgswcpts{% - % Called by \count@pgswpts - % \docount@pgswcpts is used for combined gradetable. - % Count the number of pages in range with any kind of point (bonus - % or non-bonus): - \addtocounter{@iterator}{1}% - \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \addtocounter{num@cols}{1}% + % We hold off on putting in the "\\ \hline" because we may want to + % immediately follow it with either an "\end{tabular}" or another + % "\hline". + % Scores? + \if@scores + \\ + \hline + \if@bonus + \@bhsword \hidden@ampersand + \else + \@hsword \hidden@ampersand + \fi + \setcounter{cols@done}{0}% + \do@sloop@h + \fi + \ifnum \value{current@row} = \value{num@rows}\relax + % This is the last line! End the tabular: + \\ + \hline + \end{tabular}% \else - \check@bnsptpage + % Don't end the tabular: + \\ + \hline\hline \fi - \ifnum \the@iterator < \tbl@lastp\relax - \let\next@docount=\docount@pgswcpts + % Check if we should repeat: + \ifnum \value{current@row} < \value{num@rows}\relax + \let\nextdo@lines@h=\do@lines@h \else - \let\next@docount=\relax + \let\nextdo@lines@h=\relax \fi - \next@docount -}% docount@pgswcpts -\def\check@bnsptpage{% - % We don't understand why we need to hide this inside of a macro: - \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \addtocounter{num@cols}{1}% + \nextdo@lines@h +}% do@lines@h + +\def\do@comblines@h{% + % Called only by \@multirowtable. + % Combined tables. + \addtocounter{current@row}{1}% Set to the number of the current row + \iftbl@pgs + \@chpgword + \else + \@chqword \fi -}% check@bnsptpage - -\def\docount@pgswpts{% - % Called by \count@pgswpts - % \docount@pgswpts is used for non-bonus gradetable: - \addtocounter{@iterator}{1}% - \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \addtocounter{num@cols}{1}% + \setcounter{cols@done}{0}% + \do@pq@indexloop@h + % When we finish \do@pq@indexloop@h, either we've finished a + % complete row of page (or question) numbers, or we've done all + % the page (or question) numbers through \last@pq@index, or both: + \ifnum \value{cols@done} < \value{num@cols}\relax + % We've inserted all the question (or page) numbers, and there's + % room remaining on the current line for \@chtword: + \ifnum \value{current@row} = \value{num@rows}\relax + % This is the last row; put in the total: + \do@htword@h + \else + % This isn't the last row. We insert (\value{num@cols} - + % \value{cols@done}) ampersands. + \setcounter{@iterator}{\value{num@cols}}% + \addtocounter{@iterator}{-\value{cols@done}}% + \do@emptycols@h + \fi \fi - \ifnum \the@iterator < \tbl@lastp\relax - \let\next@docount=\docount@pgswpts + \\ + \hline + % Point values go here! + \@chpword + \setcounter{cols@done}{0}% + \do@ptloop@h + % When we finish \do@ptloop@h, either we've finished a complete + % row of point values, or we've done all the question (or page) + % numbers through \last@pq@index, or both: + \ifnum \value{cols@done} < \value{num@cols}\relax + % We've inserted all the point values, and there's room + % remaining on the current line for Total Points: + \ifnum \value{current@row} = \value{num@rows}\relax + % This is the last row; put in the total: + \do@totalpts@h + \else + % This isn't the last row. We insert (\value{num@cols} - + % \value{cols@done}) ampersands. + \setcounter{@iterator}{\value{num@cols}}% + \addtocounter{@iterator}{-\value{cols@done}}% + \do@emptycols@h + \fi + \fi + \\ + \hline + % Bonus point values go here! + \@chbpword + \setcounter{cols@done}{0}% + \do@bptloop@h + % When we finish \do@bptloop@h, either + % we've finished a complete row of point values, or we've done all + % the question (or page) numbers through \last@pq@index, or both: + \ifnum \value{cols@done} < \value{num@cols}\relax + % We've inserted all the point values, and there's room + % remaining on the current line for Total Points: + \ifnum \value{current@row} = \value{num@rows}\relax + % This is the last row; put in the total: + \do@totalbpts@h + \else + % This isn't the last row. We insert (\value{num@cols} - + % \value{cols@done}) ampersands. + \setcounter{@iterator}{\value{num@cols}}% + \addtocounter{@iterator}{-\value{cols@done}}% + \do@emptycols@h + \fi + \fi + % We hold off on putting in the "\\ \hline" because we may want to + % immediately follow it with either an "\end{tabular}" or another + % "\hline". + % Scores? + \if@scores + \\ + \hline + \@chsword \hidden@ampersand + \setcounter{cols@done}{0}% + \do@sloop@h + \fi + \ifnum \value{current@row} = \value{num@rows}\relax + % This is the last line! End the tabular: + \\ + \hline + \end{tabular}% \else - \let\next@docount=\relax + % Don't end the tabular: + \\ + \hline\hline \fi - \next@docount -}% docount@pgswpts - -\def\docount@pgswbpts{% - % Called by \count@pgswpts - % \docount@pgswbpts is used for bonusgradetable: - \addtocounter{@iterator}{1}% - \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \addtocounter{num@cols}{1}% + % Check if we should repeat: + \ifnum \value{current@row} < \value{num@rows}\relax + \let\nextdo@comblines@h=\do@comblines@h + \else + \let\nextdo@comblines@h=\relax \fi - \ifnum \the@iterator < \tbl@lastp\relax - \let\next@docount=\docount@pgswbpts + \nextdo@comblines@h +}% do@comblines@h + +\def\do@pq@indexloop@h{% + % Called by both \do@lines@h and \do@comblines@h. + % We insert at most one row of pq@index: + \increment@index{pq@index}% + \ifnum \value{pq@index} > \last@pq@index\relax + % Do nothing! \else - \let\next@docount=\relax + \hidden@ampersand + \refto@index{pq@index}% + \addtocounter{cols@done}{1}% \fi - \next@docount -}% docount@pgswbpts - -%-------------------------------------------------------------------- - -\def\@htblpages{% - % We get here from \@whchtblpgs. - % Horizontal grade or point table, by pages: - \begingroup - \renewcommand\arraystretch{\@gtblstretch}% - % Set num@cols equal to the number of pages with points (of - % correct type): - \count@pgswpts - % Suppress ``extra alignment tab'' errors if \thenum@cols = 0: - \ifnum \thenum@cols = 0\relax - \setcounter{num@cols}{1}% + \ifnum \value{pq@index} < \last@pq@index\relax + \ifnum \value{cols@done} < \value{num@cols}\relax + \let\nextdo@pq@indexloop@h=\do@pq@indexloop@h + \else + \let\nextdo@pq@indexloop@h=\relax \fi - \set@hlfcntr{tbl@points}{0}% - \set@hlfcntr{tbl@bonuspoints}{0}% - \if@bonus - % Horizontal bonus grade or point table, by pages: - \begin{tabular}{|l|*{\thenum@cols}{c|}c|} - \hline - {\@bhpgword}& \setcounter{@iterator}{\tbl@firstp}% - \addtocounter{@iterator}{-1}\do@bpgnumloop - {\@bhtword}\\ - \hline - {\@bhpword}& \setcounter{@iterator}{\tbl@firstp}% - \addtocounter{@iterator}{-1}\do@bpgptloop - \prt@tablebonuspoints\\ - \hline - % If it's a grade table, do the line for scores: - \if@scores - {\@bhsword}& \setcounter{@iterator}{\tbl@firstp}% - \addtocounter{@iterator}{-1}\do@bpgsloop - \\ - \hline - \fi - \end{tabular}% + \else + \let\nextdo@pq@indexloop@h=\relax + \fi + \nextdo@pq@indexloop@h +}% do@pq@indexloop@h + +\def\do@ptloop@h{% + % Called by both \do@lines@h and \do@comblines@h. + % We insert at most one row of non-bonus point values: + \increment@index{pq@index@pts}% + \ifnum \value{pq@index@pts} > \last@pq@index\relax + % Do nothing! + \else + \hidden@ampersand + \addtocounter{cols@done}{1}% + \pointsof@index{pq@index@pts}% + \addto@hlfcntr{tbl@points}{\pointsof@index{pq@index@pts}}% + \fi + \ifnum \value{pq@index@pts} < \last@pq@index\relax + \ifnum \value{cols@done} < \value{num@cols}\relax + \let\nextdo@ptloop@h=\do@ptloop@h \else - \if@combined - % Horizontal combined grade or point table, by pages: - \begin{tabular}{|l|*{\thenum@cols}{c|}c|} - \hline - {\@chpgword}& \setcounter{@iterator}{\tbl@firstp}% - \addtocounter{@iterator}{-1}\do@cpgnumloop - {\@chtword}\\ - \hline - {\@chpword}& \setcounter{@iterator}{\tbl@firstp}% - \addtocounter{@iterator}{-1}\do@cpgptloop - \prt@tablepoints\\ - \hline - {\@chbpword}& \setcounter{@iterator}{\tbl@firstp}% - \addtocounter{@iterator}{-1}\do@cbpgptloop - \prt@tablebonuspoints\\ - \hline - % If it's a grade table, do the line for scores: - \if@scores - {\@chsword}& \setcounter{@iterator}{\tbl@firstp}% - \addtocounter{@iterator}{-1}\do@cpgsloop - \\ - \hline - \fi - \end{tabular}% - \else - % Horizontal non-bonus grade or point table, by pages: - \begin{tabular}{|l|*{\thenum@cols}{c|}c|} - \hline - {\@hpgword}& \setcounter{@iterator}{\tbl@firstp}% - \addtocounter{@iterator}{-1}\do@pgnumloop - {\@htword}\\ - \hline - {\@hpword}& \setcounter{@iterator}{\tbl@firstp}% - \addtocounter{@iterator}{-1}\do@pgptloop - \prt@tablepoints\\ - \hline - % If it's a grade table, do the line for scores: - \if@scores - {\@hsword}& \setcounter{@iterator}{\tbl@firstp}% - \addtocounter{@iterator}{-1}\do@pgsloop - \\ - \hline - \fi - \end{tabular}% - \fi + \let\nextdo@ptloop@h=\relax \fi - \endgroup -}% @htblpages - -%-------------------------------------------------------------------- -% \do@pgnumloop, \do@pgptloop, and \do@pgsloop -% are only for non-bonus gradetable. -% \pg@line is also for combinedgradetable. -\def\do@pgnumloop{% - \addtocounter{@iterator}{1}% - \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \pg@line + \else + \let\nextdo@ptloop@h=\relax \fi - \ifnum \the@iterator < \tbl@lastp\relax - \let\next@pgnumloop=\do@pgnumloop + \nextdo@ptloop@h +}% do@ptloop@h + +\def\do@bptloop@h{% + % Called by both \do@lines@h and \do@comblines@h. + % We insert at most one row of bonus point values: + \increment@index{pq@index@bpts}% + \ifnum \value{pq@index@bpts} > \last@pq@index\relax + % Do nothing! \else - \let\next@pgnumloop=\relax + \hidden@ampersand + \addtocounter{cols@done}{1}% + \bonuspointsof@index{pq@index@bpts}% + \addto@hlfcntr{tbl@bonuspoints}{\bonuspointsof@index{pq@index@bpts}}% \fi - \next@pgnumloop -}% \do@pgnumloop -\def\pg@line{% - % We still don't know why we need to hide this inside of a macro: - \pageref{firstpoints@onpage@\arabic{@iterator}}& -}% \pg@line - -\def\do@pgptloop{% - \addtocounter{@iterator}{1}% - \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \add@hlfcntrtohlfcntr{tbl@points}{tmp@hlfcntr}% - \pgpt@entry + \ifnum \value{pq@index@bpts} < \last@pq@index\relax + \ifnum \value{cols@done} < \value{num@cols}\relax + \let\nextdo@bptloop@h=\do@bptloop@h + \else + \let\nextdo@bptloop@h=\relax + \fi + \else + \let\nextdo@bptloop@h=\relax \fi - \ifnum \the@iterator < \tbl@lastp\relax - \let\next@pgptloop=\do@pgptloop + \nextdo@bptloop@h +}% do@bptloop@h + +\def\do@htword@h{% + % Called by both \do@lines@h and \do@comblines@h. + % We insert (\value{num@cols} - \value{cols@done}) ampersands, + % and then either \@htword or \@bhtword or \@chtword: + \setcounter{@iterator}{\value{num@cols}}% + \addtocounter{@iterator}{-\value{cols@done}}% + \do@emptycols@h + \if@combined + \@chtword \else - \let\next@pgptloop=\relax + \if@bonus + \@bhtword + \else + \@htword + \fi \fi - \next@pgptloop -}% \do@pgptloop -% \pgpt@entry is used by different macros: -\def\pgpt@entry{% - % We still don't know why we need to hide this inside of a macro: - \pointsonpage{\the@iterator}& -}% \pgpt@entry - -\def\do@pgsloop{% - \addtocounter{@iterator}{1}% - \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \pg@sentry +}% do@htword@h + +\def\do@totalpts@h{% + % Called by both \do@lines@h and \do@comblines@h. + % We insert (\value{num@cols} - \value{cols@done}) ampersands + % and then the total points: + \setcounter{@iterator}{\value{num@cols}}% + \addtocounter{@iterator}{-\value{cols@done}}% + \do@emptycols@h + \prt@tablepoints +}% do@totalpts@h + +\def\do@totalbpts@h{% + % Called by both \do@lines@h and \do@comblines@h. + % We insert (\value{num@cols} - \value{cols@done}) ampersands, + % and then the total bonus points: + \setcounter{@iterator}{\value{num@cols}}% + \addtocounter{@iterator}{-\value{cols@done}}% + \do@emptycols@h + \prt@tablebonuspoints +}% do@totalbpts@h + +\def\do@emptycols@h{% + % Called by \do@lines@h, \do@comblines@h, \do@htword@h, + % \do@totalpts@h, and \do@totalbpts@h. + % We insert \value{@iterator} ampersands: + \ifnum \value{@iterator} > 0\relax + \hidden@ampersand + \addtocounter{@iterator}{-1}% + \let\nextdo@emptycols@h=\do@emptycols@h + \else + \let\nextdo@emptycols@h=\relax \fi - \ifnum \the@iterator < \tbl@lastp\relax - \let\next@pgsloop=\do@pgsloop + \nextdo@emptycols@h +}% do@emptycols@h + +\def\do@sloop@h{% + % Called by both \do@lines@h and \do@comblines@h. + % We assume that cols@done has been set to zero. + % We insert num@cols \hbox to \@cellwidth, + % separated by ampersands. + \addtocounter{cols@done}{1}% + \hbox to \@cellwidth{\hfill}% + \ifnum \value{cols@done} < \value{num@cols}\relax + \hidden@ampersand + \let\nextdo@sloop@h=\do@sloop@h \else - \let\next@pgsloop=\relax + \let\nextdo@sloop@h=\relax \fi - \next@pgsloop -}% \do@pgsloop -% \pg@sentry is called by several different macros, -% to create different tables: -\def\pg@sentry{% - % We still don't know why we need to hide this inside of a macro: - \hbox to \@cellwidth{\hfill}& -}% \pg@sentry + \nextdo@sloop@h +}% do@sloop@h + %-------------------------------------------------------------------- -% These things are for bonusgradetable: +%-------------------------------------------------------------------- +% Multicolumn tables -% \do@bpgnumloop, \do@bpgptloop, \do@bpgsloop, -% and \bpg@sline are only for bonusgradetable. -% \bpg@line is also used for combinedgradetable. -\def\do@bpgnumloop{% - \addtocounter{@iterator}{1}% - \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \bpg@line - \fi - \ifnum \the@iterator < \tbl@lastp\relax - \let\next@bpgnumloop=\do@bpgnumloop + +%-------------------------------------------------------------------- +% Here's an example of a multicolumn grade table indexed by questions, +% to show how we're getting the \cline's to link up correctly with the +% \vline's. + +% Every line of \cline's is followed by a +% \noalign{\vskip\arrayrulewidth} to cancel the +% \noalign{\vskip-\arrayrulewidth} that ends the definition of +% \cline. + +% \begin{tabular}{*2{@{}c|c|c@{}c}} +% \cline{1-3} \cline{5-7} +% \noalign{\vskip\arrayrulewidth} +% \vline \hspace*{\tabcolsep}\hspace*{\fill}% +% Question% +% \hspace*{\fill}% +% & Points% +% & \hspace*{\fill}% +% Score% +% \hspace*{\tabcolsep}\hspace*{\fill}\vline +% & \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}% +% & \vline \hspace*{\tabcolsep}\hspace*{\fill}% +% Question% +% \hspace*{\fill}% +% & Points% +% & \hspace*{\fill}% +% Score% +% \hspace*{\tabcolsep}\hspace*{\fill}\vline\\ +% \cline{1-3} \cline{5-7} +% \noalign{\vskip\arrayrulewidth} +% \vline \hspace*{\tabcolsep}\hspace*{\fill}% +% 1\hspace*{\fill}% +% & 5% +% & \hspace*{\fill}% +% \hbox to \@cellwidth{\hfill}% +% \hspace*{\tabcolsep}\hspace*{\fill}\vline +% & \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}% +% & \vline\hspace*{\tabcolsep}\hspace*{\fill}% +% 4% +% \hspace*{\fill}% +% & 20% +% & \hspace*{\fill}% +% \hbox to \@cellwidth{\hfill}% +% \hspace*{\tabcolsep}\hspace*{\fill}\vline\\ +% \cline{1-3} \cline{5-7} +% \noalign{\vskip\arrayrulewidth} +% \vline \hspace*{\tabcolsep}\hspace*{\fill}% +% 2% +% \hspace*{\fill}% +% & 10% +% & \hspace*{\fill}% +% \hbox to \@cellwidth{\hfill}% +% \hspace*{\tabcolsep}\hspace*{\fill}\vline +% & \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}% +% & \vline\hspace*{\tabcolsep}\hspace*{\fill}% +% 5% +% \hspace*{\fill}% +% & 25% +% & \hspace*{\fill}% +% \hbox to \@cellwidth{\hfill}% +% \hspace*{\tabcolsep}\hspace*{\fill}\vline\\ +% \cline{1-3} \cline{5-7} +% \noalign{\vskip\arrayrulewidth} +% \vline \hspace*{\tabcolsep}\hspace*{\fill}% +% 3% +% \hspace*{\fill}% +% & 15% +% & \hspace*{\fill}% +% \hbox to \@cellwidth{\hfill}% +% \hspace*{\tabcolsep}\hspace*{\fill}\vline +% & \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}% +% & \vline\hspace*{\tabcolsep}\hspace*{\fill}% +% Total:% +% \hspace*{\fill}% +% & 75% +% & \hspace*{\fill}% +% \hbox to \@cellwidth{\hfill}% +% \hspace*{\tabcolsep}\hspace*{\fill}\vline\\ +% \cline{1-3} \cline{5-7} +% \noalign{\vskip\arrayrulewidth} +% \end{tabular} +%-------------------------------------------------------------------- +%-------------------------------------------------------------------- +% Check that the number of cols is OK, and compute the number of rows: + +\def\check@num@cols@v{% + % We get here from \tbl@v@or@h. + % We make sure the number of cols is between 1 and 10 (since we + % can't handle more than 10 cols in a multicolumn table). + % If it is, we go on to \@computenumrows@v + \ifnum \value{num@cols} < 1\relax + \ClassError{exam}{% + The number of columns in a table must be positive.\MessageBreak + }{% + The number of columns must be a positive integer.\MessageBreak + }% + \fbox{\textbf{Error:} Multicolumn table with no columns!}% \else - \let\next@bpgnumloop=\relax - \fi - \next@bpgnumloop -}% do@bpgnumloop -\def\bpg@line{% - % We still don't know why we need to hide this inside of a macro: - \pageref{firstbonuspoints@onpage@\arabic{@iterator}}& -}% bpg@line - -\def\do@bpgptloop{% - \addtocounter{@iterator}{1}% - \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \add@hlfcntrtohlfcntr{tbl@bonuspoints}{tmp@hlfcntr}% - \bpgpt@line + \ifnum \value{num@cols} > 10\relax + \ClassError{exam}{% + Multicolumn tables can have at most 10 columns.\MessageBreak + }{% + Multicolumn tables can have at most 10 columns.\MessageBreak + }% + \fbox{\textbf{Error:} Multicolumn table with more than 10 columns!}% + \else + \@computenumrows@v + \fi \fi - \ifnum \the@iterator < \tbl@lastp\relax - \let\next@bpgptloop=\do@bpgptloop +}% check@num@cols@v + +\def\@computenumrows@v{% + % We get here from \check@num@cols@v. + % Compute the number of rows. + % First: set num@rows to one more than the number of either + % (questions) or (pages with the appropriate type of points), to + % have slots for the total along with the questions or page numbers: + \iftbl@pgs + \count@pgswpts{num@rows}% + \addtocounter{num@rows}{1}% \else - \let\next@bpgptloop=\relax + \setcounter{num@rows}{\last@pq@index}% + \addtocounter{num@rows}{-\first@pq@index}% + \addtocounter{num@rows}{2}% \fi - \next@bpgptloop -}% do@bpgptloop -% \bpgpt@line is called by several macros: -\def\bpgpt@line{% - % We still don't know why we need to hide this inside of a macro: - \bonuspointsonpage{\the@iterator}& -}% bpgpt@line - -\def\do@bpgsloop{% - \addtocounter{@iterator}{1}% - \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \pg@sentry + % Save the number of slots needed, using pq@index@bpts as a scratch + % counter, to check for truncation on division: + \setcounter{pq@index@bpts}{\value{num@rows}}% + % Divide the number of slots needed by num@cols: + \divide \csname c@num@rows\endcsname by + \csname c@num@cols\endcsname + % Division truncates: See if there was truncation. + % Use the counter @iterator as a scratch counter: + \setcounter{@iterator}{\value{num@rows}}% + \multiply \csname c@@iterator\endcsname by + \csname c@num@cols\endcsname + \ifnum \value{@iterator} < \value{pq@index@bpts}\relax + % There was truncation; add one to num@rows: + \addtocounter{num@rows}{1}% \fi - \ifnum \the@iterator < \tbl@lastp\relax - \let\next@bpgsloop=\do@bpgsloop + \@multicolumntable +}% @computenumrows@v + +%-------------------------------------------------------------------- +% Construct the actual table: + +\def\@multicolumntable{% + % We get here from \@computenumrows@v. + % Set \cline@stuff@v equal to the line of \cline's: + \create@cline@stuff@v + \renewcommand\arraystretch{\@gtblstretch}% + \set@hlfcntr{tbl@points}{0}% + \set@hlfcntr{tbl@bonuspoints}{0}% + \if@combined + \if@scores + % combinedgradetable, possibly partial. + % Note: We'll never use the final "c" in the format of the + % tabular, but there's no harm in that. + \begin{tabular}{*{\value{num@cols}}{@{}c|c|c|c@{}c}} + % We need to make sure that the \cline@stuff@v commands come + % *immediately* following the \\ or \begin{tabular} (with no + % conditionals evaluated, even if those conditionals expand to + % the empty string)! + % Put in the row of column headings, with \cline@stuff@v above and + % below: + \cline@stuff@v + \setcounter{@iterator}{0}% + \docolumn@heads@comb@v + \\ + \cline@stuff@v + \else + % combinedpointtable, possibly partial. + % Note: We'll never use the final "c" in the format of the + % tabular, but there's no harm in that. + \begin{tabular}{*{\value{num@cols}}{@{}c|c|c@{}c}} + % We need to make sure that the \cline@stuff@v commands come + % *immediately* following the \\ or \begin{tabular} (with no + % conditionals evaluated, even if those conditionals expand to + % the empty string)! + % Put in the row of column headings, with \cline@stuff@v above and + % below: + \cline@stuff@v + \setcounter{@iterator}{0}% + \docolumn@heads@comb@noscores@v + \\ + \cline@stuff@v + \fi + % pq@index@pts will hold the question number (or page number) in + % the first column of the row. + \setcounter{pq@index@pts}{\first@pq@index}% + \iftbl@pgs + % If we're indexed by pages, we need to make sure there are + % points of the appropriate type on the first page listed: + \addtocounter{pq@index@pts}{-1}% + \find@nextpagewithpoints{pq@index@pts}% + \fi + \setcounter{current@row}{0}% + \do@lines@v \else - \let\next@bpgsloop=\relax + % It's not combined: + \if@scores + % Note: We'll never use the final "c" in the format of the + % tabular, but there's no harm in that. + \begin{tabular}{*{\value{num@cols}}{@{}c|c|c@{}c}} + % We need to make sure that the \cline@stuff@v commands come + % *immediately* following the \\ or \begin{tabular} (with no + % conditionals evaluated, even if those conditionals expand to + % the empty string)! + % Put in the row of column headings, with \cline@stuff@v above and + % below: + \cline@stuff@v + \setcounter{@iterator}{0}% + \docolumn@heads@v + \\ + \cline@stuff@v + \else + % Note: We'll never use the final "c" in the format of the + % tabular, but there's no harm in that. + \begin{tabular}{*{\value{num@cols}}{@{}c|c@{}c}} + % We need to make sure that the \cline@stuff@v commands come + % *immediately* following the \\ or \begin{tabular} (with no + % conditionals evaluated, even if those conditionals expand to + % the empty string)! + % Put in the row of column headings, with \cline@stuff@v above and + % below: + \cline@stuff@v + \setcounter{@iterator}{0}% + \docolumn@heads@noscores@v + \\ + \cline@stuff@v + \fi + % pq@index@pts will hold the question number (or page number) in + % the first column of the row. + \setcounter{pq@index@pts}{\first@pq@index}% + \iftbl@pgs + % If we're indexed by pages, we need to make sure there are + % points of the appropriate type on the first page listed: + \addtocounter{pq@index@pts}{-1}% + \find@nextpagewithpoints{pq@index@pts}% + \fi + \setcounter{current@row}{0}% + \do@lines@v \fi - \next@bpgsloop -}% do@bpgsloop +}% @multicolumntable %-------------------------------------------------------------------- -% These things are for combinedgradetable: +% \create@cline@stuff@v + +% The function \create@cline@stuff@v defines \cline@stuff@v to be whatever's +% appropriate given the values of num@cols, \if@bonus, \if@combined, and +% \if@scores. + +% We wimped out of generating \cline@stuff@v on the fly because we didn't +% see how to get the correct expansions/nonexpansions without using a +% primitive of e-TeX. + +% \clines@ii@whatever is for tables in which a logical column consists +% of two columns; it's used for pointtable and bonuspointtable. + +\def\clines@ii@i{\cline{1-2}} +\def\clines@ii@ii{\cline{1-2} \cline{4-5}} +\def\clines@ii@iii{\cline{1-2} \cline{4-5} \cline{7-8}} +\def\clines@ii@iv{\cline{1-2} \cline{4-5} \cline{7-8} \cline{10-11}} +\def\clines@ii@v{\cline{1-2} \cline{4-5} \cline{7-8} \cline{10-11} + \cline{13-14}} +\def\clines@ii@vi{\cline{1-2} \cline{4-5} \cline{7-8} \cline{10-11} + \cline{13-14} \cline{16-17}} +\def\clines@ii@vii{\cline{1-2} \cline{4-5} \cline{7-8} \cline{10-11} + \cline{13-14} \cline{16-17} \cline{19-20}} +\def\clines@ii@viii{\cline{1-2} \cline{4-5} \cline{7-8} \cline{10-11} + \cline{13-14} \cline{16-17} \cline{19-20} \cline{22-23}} +\def\clines@ii@vix{\cline{1-2} \cline{4-5} \cline{7-8} \cline{10-11} + \cline{13-14} \cline{16-17} \cline{19-20} \cline{22-23} + \cline{25-26}} +\def\clines@ii@x{\cline{1-2} \cline{4-5} \cline{7-8} \cline{10-11} + \cline{13-14} \cline{16-17} \cline{19-20} \cline{22-23} + \cline{25-26} \cline{28-29}} + +% \clines@iii@whatever is for tables in which a logical column consists +% of three columns; it's are used for gradetable, bonusgradetable, and +% combinedpointtable: + +\def\clines@iii@i{\cline{1-3}} +\def\clines@iii@ii{\cline{1-3} \cline{5-7}} +\def\clines@iii@iii{\cline{1-3} \cline{5-7} \cline{9-11}} +\def\clines@iii@iv{\cline{1-3} \cline{5-7} \cline{9-11} \cline{13-15}} +\def\clines@iii@v{\cline{1-3} \cline{5-7} \cline{9-11} \cline{13-15} + \cline{17-19}} +\def\clines@iii@vi{\cline{1-3} \cline{5-7} \cline{9-11} \cline{13-15} + \cline{17-19} \cline{21-23}} +\def\clines@iii@vii{\cline{1-3} \cline{5-7} \cline{9-11} \cline{13-15} + \cline{17-19} \cline{21-23} \cline{25-27}} +\def\clines@iii@viii{\cline{1-3} \cline{5-7} \cline{9-11} \cline{13-15} + \cline{17-19} \cline{21-23} \cline{25-27} \cline{29-31}} +\def\clines@iii@ix{\cline{1-3} \cline{5-7} \cline{9-11} \cline{13-15} + \cline{17-19} \cline{21-23} \cline{25-27} \cline{29-31} + \cline{33-35}} +\def\clines@iii@x{\cline{1-3} \cline{5-7} \cline{9-11} \cline{13-15} + \cline{17-19} \cline{21-23} \cline{25-27} \cline{29-31} + \cline{33-35} \cline{37-39}} + + +% \clines@iv@whatever is for tables in which a logical column +% consists of four columns; it's used for combinedgradetable. + +\def\clines@iv@i{\cline{1-4}} +\def\clines@iv@ii{\cline{1-4} \cline{6-9}} +\def\clines@iv@iii{\cline{1-4} \cline{6-9} \cline{11-14}} +\def\clines@iv@iv{\cline{1-4} \cline{6-9} \cline{11-14} \cline{16-19}} +\def\clines@iv@v{\cline{1-4} \cline{6-9} \cline{11-14} \cline{16-19} + \cline{21-24}} +\def\clines@iv@vi{\cline{1-4} \cline{6-9} \cline{11-14} \cline{16-19} + \cline{21-24} \cline{26-29}} +\def\clines@iv@vii{\cline{1-4} \cline{6-9} \cline{11-14} \cline{16-19} + \cline{21-24} \cline{26-29} \cline{31-34}} +\def\clines@iv@viii{\cline{1-4} \cline{6-9} \cline{11-14} \cline{16-19} + \cline{21-24} \cline{26-29} \cline{31-34} \cline{36-39}} +\def\clines@iv@ix{\cline{1-4} \cline{6-9} \cline{11-14} \cline{16-19} + \cline{21-24} \cline{26-29} \cline{31-34} \cline{36-39} + \cline{41-44}} +\def\clines@iv@x{\cline{1-4} \cline{6-9} \cline{11-14} \cline{16-19} + \cline{21-24} \cline{26-29} \cline{31-34} \cline{36-39} + \cline{41-44} \cline{46-49}} + +% The definition of \cline ends with \noalign{\vskip-\arrayrulewidth}, +% and so we want to throw in a \noalign{\vskip\arrayrulewidth} to +% cancel that. +\def\cline@correction{\noalign{\vskip\arrayrulewidth}} + +\def\create@cline@stuff@v{% + % Called by \@multicolumntable. + \if@combined + \if@scores + \edef\cline@stuff@v{\expandafter\noexpand\csname + clines@iv@\romannumeral \c@num@cols\endcsname + \noexpand\cline@correction}% + \else + \edef\cline@stuff@v{\expandafter\noexpand\csname + clines@iii@\romannumeral \c@num@cols\endcsname + \noexpand\cline@correction}% + \fi + \else + \if@scores + \edef\cline@stuff@v{\expandafter\noexpand\csname + clines@iii@\romannumeral \c@num@cols\endcsname + \noexpand\cline@correction}% + \else + \edef\cline@stuff@v{\expandafter\noexpand\csname + clines@ii@\romannumeral \c@num@cols\endcsname + \noexpand\cline@correction}% + \fi + \fi +}% create@cline@stuff@v -% \do@cpgnumloop, \cpg@line, \cbpg@line, \do@cpgptloop, \bpgpt@line, -% \do@cpgsloop, \bpg@sline, and \do@cbpgptloop are only for -% combinedgradetable: -\def\do@cpgnumloop{% +%-------------------------------------------------------------------- +% The various \docolumn@heads@something@v + +\def\docolumn@heads@v{% + % Called by \@multicolumntable. + % multicolumngradetable or multicolumnbonusgradetable, possibly + % partial. + \vline \hspace*{\tabcolsep}\hspace*{\fill}% + \iftbl@pgs + \if@bonus + \@bvpgword + \else + \@vpgword + \fi + \else + \if@bonus + \@bvqword + \else + \@vqword + \fi + \fi + \hspace*{\fill}% + & \if@bonus + \@bvpword + \else + \@vpword + \fi + & \hspace*{\fill}% + \if@bonus + \@bvsword + \else + \@vsword + \fi + \hspace*{\tabcolsep}\hspace*{\fill}\vline \addtocounter{@iterator}{1}% - \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \pg@line + \ifnum \value{@iterator} < \value{num@cols}\relax + \hidden@ampersand + \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}% + \hidden@ampersand + \let\nextdocolumn@heads@v=\docolumn@heads@v \else - \check@bpgpts + \let\nextdocolumn@heads@v=\relax \fi - \ifnum \the@iterator < \tbl@lastp\relax - \let\next@cpgnumloop=\do@cpgnumloop + \nextdocolumn@heads@v +}% docolumn@heads@v + +\def\docolumn@heads@noscores@v{% + % Called by \@multicolumntable. + % multicolumnpointtable or multicolumnbonuspointtable, possibly + % partial. + \vline \hspace*{\tabcolsep}\hspace*{\fill}% + \iftbl@pgs + \if@bonus + \@bvpgword + \else + \@vpgword + \fi + \else + \if@bonus + \@bvqword + \else + \@vqword + \fi + \fi + \hspace*{\fill}% + & \hspace*{\fill}% + \if@bonus + \@bvpword + \else + \@vpword + \fi + \hspace*{\tabcolsep}\hspace*{\fill}\vline + \addtocounter{@iterator}{1}% + \ifnum \value{@iterator} < \value{num@cols}\relax + \hidden@ampersand + \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}% + \hidden@ampersand + \let\nextdocolumn@heads@noscores@v=\docolumn@heads@noscores@v \else - \let\next@cpgnumloop=\relax + \let\nextdocolumn@heads@noscores@v=\relax \fi - \next@cpgnumloop -}% do@bpgnumloop -\def\check@bpgpts{% - \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \bpg@line + \nextdocolumn@heads@noscores@v +}% docolumn@heads@noscores@v + +\def\docolumn@heads@comb@v{% + % Called by \@multicolumntable. + % multicolumncombinedgradetable, possibly partial. + \vline \hspace*{\tabcolsep}\hspace*{\fill}% + \iftbl@pgs + \@cvpgword + \else + \@cvqword + \fi + \hspace*{\fill}% + & \@cvpword + & \@cvbpword + & \hspace*{\fill}% + \@cvsword + \hspace*{\tabcolsep}\hspace*{\fill}\vline + \addtocounter{@iterator}{1}% + \ifnum \value{@iterator} < \value{num@cols}\relax + \hidden@ampersand + \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}% + \hidden@ampersand + \let\nextdocolumn@heads@comb@v=\docolumn@heads@comb@v + \else + \let\nextdocolumn@heads@comb@v=\relax \fi -}% check@bpgpts - -\def\do@cpgptloop{% + \nextdocolumn@heads@comb@v +}% docolumn@heads@comb@v + +\def\docolumn@heads@comb@noscores@v{% + % Called by \@multicolumntable. + % multicolumncombinedpointtable, possibly partial. + \vline \hspace*{\tabcolsep}\hspace*{\fill}% + \iftbl@pgs + \@cvpgword + \else + \@cvqword + \fi + \hspace*{\fill}% + & \@vpword + & \hspace*{\fill}% + \@bvpword + \hspace*{\tabcolsep}\hspace*{\fill}\vline \addtocounter{@iterator}{1}% - \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \add@hlfcntrtohlfcntr{tbl@points}{tmp@hlfcntr}% - \pgpt@entry + \ifnum \value{@iterator} < \value{num@cols}\relax + \hidden@ampersand + \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}% + \hidden@ampersand + \let\nextdocolumn@heads@comb@noscores@v=\docolumn@heads@comb@noscores@v \else - \check@cbpts + \let\nextdocolumn@heads@comb@noscores@v=\relax \fi - \ifnum \the@iterator < \tbl@lastp\relax - \let\next@cpgptloop=\do@cpgptloop + \nextdocolumn@heads@comb@noscores@v +}% docolumn@heads@comb@noscores@v + +%-------------------------------------------------------------------- +% \do@lines@v is used by *all* multicolumn tables. +% It calls \do@oneline@v for all non-combined tables and +% \do@oneline@comb@v for all combined tables. + +\def\do@lines@v{% + % We get here from \@multicolumntable. + % ALL MULTICOLUMN TABLES!!!! + % pq@index@pts holds the question number or page number in the first + % column of the current row. + \addtocounter{current@row}{1}% + \setcounter{pq@index}{\value{pq@index@pts}}% + \setcounter{cols@done}{0}% Number of columns done + % We're doing both grade tables and point tables!! + \if@combined + \do@oneline@comb@v \else - \let\next@cpgptloop=\relax + \do@oneline@v \fi - \next@cpgptloop -}% do@cpgptloop -\def\check@cbpts{% - \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \pgpt@entry + \increment@index{pq@index@pts}% + % We need the "\\ \cline@stuff@v" to *immediately* precede the + % \end{tabular} (i.e., with no \ifnum separating them), to avoid + % having crap after the \cline@stuff@v that + % causes there to be an extra row at the end of the table. We also + % need there to be nothing between \\ and \cline@stuff@v. + \ifnum \value{current@row} = \value{num@rows}\relax + \\ + \cline@stuff@v + \end{tabular}% + \let\nextdo@lines@v=\relax + \else + \\ + \cline@stuff@v + \let\nextdo@lines@v=\do@lines@v \fi -}% check@cbpts - -\def\do@cbpgptloop{% - \addtocounter{@iterator}{1}% - \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \add@hlfcntrtohlfcntr{tbl@bonuspoints}{tmp@hlfcntr}% - \bpgpt@line + \nextdo@lines@v +}% do@lines@v + +\def\do@oneline@v{% + % Called by \do@lines@v. + % Used for all multicolumn non-combined tables. + % pq@index holds the question or page number we're about to do. + \ifnum \value{pq@index} > \last@pq@index\relax + % See if we're in the last column; use pq@index@bpts as a scratch + % counter: + \setcounter{pq@index@bpts}{\value{cols@done}}% + \addtocounter{pq@index@bpts}{1}% + \ifnum \value{pq@index@bpts} = \value{num@cols}\relax + % We're in the last column; are we in the last row? + \ifnum \value{current@row} = \value{num@rows}\relax + % We're in the last column, last row! + % Print the total: + \vline \hspace*{\tabcolsep}\hspace*{\fill}% + \if@bonus + \@bvtword + \else + \@vtword + \fi + \hspace*{\fill}% + \hidden@ampersand + \if@scores + \if@bonus + \prt@tablebonuspoints + \else + \prt@tablepoints + \fi + \hidden@ampersand + \hspace*{\fill}% + \hbox to \@cellwidth{\hfill}% + \hspace*{\tabcolsep}\hspace*{\fill}\vline + \else + \hspace*{\fill}% + \if@bonus + \prt@tablebonuspoints + \else + \prt@tablepoints + \fi + \hspace*{\tabcolsep}\hspace*{\fill}\vline + \fi + \else + % Not last column last row; insert empty space: + \vline \hspace*{\tabcolsep}\hspace*{\fill}% + \hbox to \@cellwidth{\hfill}% + \hspace*{\fill}% + \if@scores + \hidden@ampersand + \hbox to \@cellwidth{\hfill}% + \fi + \hidden@ampersand + \hspace*{\fill}% + \hbox to \@cellwidth{\hfill}% + \hspace*{\tabcolsep}\hspace*{\fill}\vline + \fi + \else + % Not last column; insert empty space: + \vline \hspace*{\tabcolsep}\hspace*{\fill}% + \hbox to \@cellwidth{\hfill}% + \hspace*{\fill}% + \if@scores + \hidden@ampersand + \hbox to \@cellwidth{\hfill}% + \fi + \hidden@ampersand + \hspace*{\fill}% + \hbox to \@cellwidth{\hfill}% + \hspace*{\tabcolsep}\hspace*{\fill}\vline + \fi \else - \check@cnonpts + % We need to do question (or page) number pq@index: + \vline \hspace*{\tabcolsep}\hspace*{\fill}% + \refto@index{pq@index}% + \hspace*{\fill}% + \hidden@ampersand + \if@scores + \if@bonus + \bonuspointsof@index{pq@index}% + \addto@hlfcntr{tbl@bonuspoints}{\bonuspointsof@index{pq@index}}% + \else + \pointsof@index{pq@index}% + \addto@hlfcntr{tbl@points}{\pointsof@index{pq@index}}% + \fi + \hidden@ampersand + \hspace*{\fill}% + \hbox to \@cellwidth{\hfill}% + \hspace*{\tabcolsep}\hspace*{\fill}\vline + \else + \hspace*{\fill}% + \if@bonus + \bonuspointsof@index{pq@index}% + \addto@hlfcntr{tbl@bonuspoints}{\bonuspointsof@index{pq@index}}% + \else + \pointsof@index{pq@index}% + \addto@hlfcntr{tbl@points}{\pointsof@index{pq@index}}% + \fi + \hspace*{\tabcolsep}\hspace*{\fill}\vline + \fi \fi - \ifnum \the@iterator < \tbl@lastp\relax - \let\next@cbpgptloop=\do@cbpgptloop + \addtocounter{cols@done}{1}% Number of columns done + \ifnum \value{cols@done} < \value{num@cols}\relax + \hidden@ampersand + \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}% + \nextcolumn@index@v{pq@index}% + \hidden@ampersand + \let\nextdo@oneline@v=\do@oneline@v \else - \let\next@cbpgptloop=\relax + \let\nextdo@oneline@v=\relax \fi - \next@cbpgptloop -}% do@cbpgptloop -\def\check@cnonpts{% - \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \bpgpt@line + \nextdo@oneline@v +}% do@oneline@v + +\def\do@oneline@comb@v{% + % Called by \do@lines@v. + % All combined multicolumn tables. + % pq@index holds the question (or page) we're about to do. + \ifnum \value{pq@index} > \last@pq@index\relax + % See if we're in the last column; use pq@index@bpts as a scratch + % counter: + \setcounter{pq@index@bpts}{\value{cols@done}}% + \addtocounter{pq@index@bpts}{1}% + \ifnum \value{pq@index@bpts} = \value{num@cols}\relax + % We're in the last column; are we in the last row? + \ifnum \value{current@row} = \value{num@rows}\relax + % We're in the last column, last row! + % Print the total: + \vline \hspace*{\tabcolsep}\hspace*{\fill}% + \@cvtword + \hspace*{\fill}% + \hidden@ampersand + \prt@tablepoints + \hidden@ampersand + \if@scores + \prt@tablebonuspoints + \hidden@ampersand + \hspace*{\fill}% + \hbox to \@cellwidth{\hfill}% + \hspace*{\tabcolsep}\hspace*{\fill}\vline + \else + \hspace*{\fill}% + \prt@tablebonuspoints + \hspace*{\tabcolsep}\hspace*{\fill}\vline + \fi + \else + % Last column, but not last row; insert empty space: + \vline \hspace*{\tabcolsep}\hspace*{\fill}% + \hbox to \@cellwidth{\hfill}% + \hspace*{\fill}% + \hidden@ampersand + \hbox to \@cellwidth{\hfill}% + \if@scores + \hidden@ampersand + \hbox to \@cellwidth{\hfill}% + \fi + \hidden@ampersand + \hspace*{\fill}% + \hbox to \@cellwidth{\hfill}% + \hspace*{\tabcolsep}\hspace*{\fill}\vline + \fi + \else + % Not last column; insert empty space: + \vline \hspace*{\tabcolsep}\hspace*{\fill}% + \hbox to \@cellwidth{\hfill}% + \hspace*{\fill}% + \hidden@ampersand + \hbox to \@cellwidth{\hfill}% + \if@scores + \hidden@ampersand + \hbox to \@cellwidth{\hfill}% + \fi + \hidden@ampersand + \hspace*{\fill}% + \hbox to \@cellwidth{\hfill}% + \hspace*{\tabcolsep}\hspace*{\fill}\vline + \fi + \else + % We need to do question number pq@index: + \vline \hspace*{\tabcolsep}\hspace*{\fill}% + \refto@index{pq@index}% + \hspace*{\fill}% + \hidden@ampersand + \pointsof@index{pq@index}% + \addto@hlfcntr{tbl@points}{\pointsof@index{pq@index}}% + \hidden@ampersand + \if@scores + \bonuspointsof@index{pq@index}% + \addto@hlfcntr{tbl@bonuspoints}{\bonuspointsof@index{pq@index}}% + \hidden@ampersand + \hspace*{\fill}% + \hbox to \@cellwidth{\hfill}% + \hspace*{\tabcolsep}\hspace*{\fill}\vline + \else + \hspace*{\fill}% + \bonuspointsof@index{pq@index}% + \addto@hlfcntr{tbl@bonuspoints}{\bonuspointsof@index{pq@index}}% + \hspace*{\tabcolsep}\hspace*{\fill}\vline + \fi \fi -}% check@cnonpts - -\def\do@cpgsloop{% - \addtocounter{@iterator}{1}% - \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\the@iterator}}% - \ifhlfcntr@pos{tmp@hlfcntr}% - \pg@sentry + \addtocounter{cols@done}{1}% Number of columns done + \ifnum \value{cols@done} < \value{num@cols}\relax + \hidden@ampersand + \hspace*{-\arrayrulewidth}\hspace*{\doublerulesep}% + \nextcolumn@index@v{pq@index}% + \hidden@ampersand + \let\nextdo@oneline@comb@v=\do@oneline@comb@v \else - \check@bnsline + \let\nextdo@oneline@comb@v=\relax \fi - \ifnum \the@iterator < \tbl@lastp\relax - \let\next@cpgsloop=\do@cpgsloop + \nextdo@oneline@comb@v +}% do@oneline@comb@v + +%-------------------------------------------------------------------- +% \find@nextpagewithpoints and \find@nextcolumnpage@v: + +\def\find@nextpagewithpoints#1{% + % Called by \dofind@nextcolumnpage@v, \increment@index, and + % \@multicolumntable. + % The argument #1 should be the name of a counter with a nonnegative + % value. + % We increase #1 by at least 1 to either the number of the + % next page containing the appropriate kind of points, or to something + % greater than \tbl@lastp. + \addtocounter{#1}{1}% + \if@combined + \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\value{#1}}}% + \addto@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\value{#1}}}% + % The sum is positive when at least one of them is positive. \else - \let\next@cpgsloop=\relax + \if@bonus + \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\value{#1}}}% + \else + \set@hlfcntr{tmp@hlfcntr}{\pointsonpage{\value{#1}}}% + \fi \fi - \next@cpgsloop -}% do@cpgsloop -\def\check@bnsline{% - \set@hlfcntr{tmp@hlfcntr}{\bonuspointsonpage{\the@iterator}}% \ifhlfcntr@pos{tmp@hlfcntr}% - \pg@sentry + \let\nextfind@nextpagewithpoints=\relax + \else + \ifnum \value{#1} > \tbl@lastp\relax + \let\nextfind@nextpagewithpoints=\relax + \else + \def\nextfind@nextpagewithpoints{\find@nextpagewithpoints{#1}}% + \fi \fi -}% check@bnsline + \nextfind@nextpagewithpoints +}% find@nextpagewithpoints + +\def\find@nextcolumnpage@v#1{% + % Called by \nextcolumn@index@v. + % This is used for all multicolumn tables that are indexed by + % pages. + % We use \find@nextpagewithpoints to increment #1 to either + % the (num@rows)'th page number after #1 that contains the + % appropriate kind of points or to a value greater than \tbl@lastp. + % We use pq@index@bpts as a scratch counter. + \setcounter{pq@index@bpts}{0}% + \dofind@nextcolumnpage@v{#1}% +}% find@nextcolumnpage@v +\def\dofind@nextcolumnpage@v#1{% + % Called only by \find@nextcolumnpage@v. + \addtocounter{pq@index@bpts}{1}% + \find@nextpagewithpoints{#1}% + \ifnum \value{pq@index@bpts} = \value{num@rows}\relax + \let\nextdofind@nextcolumnpage@v=\relax + \else + % The following test shouldn't be needed, in theory, because the + % computation of num@cols should prevent trouble, but we're being + % paranoid. + \ifnum \value{#1} > \tbl@lastp\relax + \let\nextdofind@nextcolumnpage@v=\relax + \else + % Note: this is a \def, and not a \let, because we need to put + % in the argument #1: + \def\nextdofind@nextcolumnpage@v{\dofind@nextcolumnpage@v{#1}}% + \fi + \fi + \nextdofind@nextcolumnpage@v +}% dofind@nextcolumnpage@v %-------------------------------------------------------------------- -% \pointsinrange and \bonuspointsinrange: +% \pointsinrange and \bonuspointsinrange, and then +% \firstqinrange, \lastqinrange, and \numqinrange. + + % We say either \@bonusfalse or \@bonustrue, and then we check it only % in \do@countloop: \def\pointsinrange#1{% \@bonusfalse \def\tbl@range{#1}% \@ifundefined{exam@numpoints}% - {\mbox{\normalfont\bf ??}}% + {\mbox{\normalfont\bfseries ??}}% {\read@range}% }% pointsinrange @@ -6211,19 +7468,22 @@ \@bonustrue \def\tbl@range{#1}% \@ifundefined{exam@numpoints}% - {\mbox{\normalfont\bf ??}}% + {\mbox{\normalfont\bfseries ??}}% {\read@range}% }% bonuspointsinrange \def\bad@range{% - {\mbox{\normalfont\bf ??}}% + % Called by \read@range, \firstqinrange, \lastqinrange, and + % \numqinrange. + {\mbox{\normalfont\bfseries ??}}% \ClassWarning{exam}{% - Grading range `\tbl@range ' not defined.\MessageBreak + Grading range `\tbl@range' not defined.\MessageBreak \space\space Run LaTeX again.\MessageBreak }% }% bad@range \def\read@range{% + % Called by \pointsinrange and \bonuspointsinrange. \@ifundefined{range@\tbl@range @firstq}% {% \bad@range @@ -6238,7 +7498,7 @@ \edef\tbl@lastq{\csname range@\tbl@range @lastq\endcsname}% % Check that firstq precedes or equals lastq: \ifnum \tbl@firstq > \tbl@lastq\relax - \fbox{Error: Grading Range `\tbl@range ': + \fbox{\textbf{Error:} Grading Range `\tbl@range ': Last question precedes first question.}% \ClassError{exam}{% In grading range `\tbl@range ', @@ -6289,26 +7549,29 @@ % \firstqinrange, \lastqinrange, and \numqinrange. \newcommand{\firstqinrange}[1]{% - \@ifundefined{range@#1@firstq}% - {\mbox{\normalfont\bf ??}}% + \def\tbl@range{#1}% + \@ifundefined{range@\tbl@range @firstq}% + {\bad@range}% {\csname range@#1@firstq\endcsname}% }% firstqinrange \newcommand{\lastqinrange}[1]{% - \@ifundefined{range@#1@lastq}% - {\mbox{\normalfont\bf ??}}% + \def\tbl@range{#1}% + \@ifundefined{range@\tbl@range @lastq}% + {\bad@range}% {\csname range@#1@lastq\endcsname}% }% lastqinrange \newcommand{\numqinrange}[1]{% + \def\tbl@range{#1}% \@ifundefined{range@#1@firstq}% {% - \mbox{\normalfont\bf ??}% + \bad@range }% {% \@ifundefined{range@#1@lastq}% {% - \mbox{\normalfont\bf ??}% + \bad@range }% {% \setcounter{@iterator}{\csname range@#1@lastq\endcsname}% @@ -6319,6 +7582,7 @@ }% }% numqinrange + %-------------------------------------------------------------------- %-------------------------------------------------------------------- % @@ -6632,12 +7896,63 @@ % much vertical space, then it will spill out of the bottom of the % box, overwriting whatever follows the box. +% 2016/02/08: The solutionbox frame can now be printed in color, as +% long as you load color.sty in the preamble. +% +% Usage: Say +% +% \usepackage{color} +% +% in the preamble, and then give the command +% +% \colorsolutionboxes +% +% to have the frame around a solutionbox in color. The default color +% was created by the command +% +% \definecolor{SolutionBoxColor}{gray}{0.8} +% +% and you can change the color by giving a new \definecolor command +% (which must be done *after* the \colorsolutionboxes command). +% +% To cancel color solutionbox frames and return to black, give the +% command +% +% \nocolorsolutionboxes + +\newif\if@colorsolutionboxes +\@colorsolutionboxesfalse +\def\colorsolutionboxes{% + \@ifundefined{definecolor} + {% + \ClassError{exam}{% + You must load the color package with the command\MessageBreak + \space\space\protect\usepackage{color}\MessageBreak + in order to use the command \protect\colorsolutionboxes + \MessageBreak + }{% + This command makes use of the package color.sty,\MessageBreak + and so you have to load color.sty before your\MessageBreak + \protect\begin{document} command.\MessageBreak + }% + }% + {% + \definecolor{SolutionBoxColor}{gray}{0.8} + \@colorsolutionboxestrue + }% +} +\def\nocolorsolutionboxes{\@colorsolutionboxesfalse} + \newbox\exam@box \newenvironment{solutionbox}[1]{% \@insolutiontrue % cancelled by the end of the environment \@addpointsfalse % cancelled by the end of the environment \def\solutionbox@size{#1}% saved for end of environment - \@tempdima=\textwidth +% Change, 2016/02/08: So that the solutionbox environment will work +% correctly inside of a tabular environment, we use \hsize instead of +% \textwidth: +% \@tempdima=\textwidth + \@tempdima=\hsize \advance\@tempdima -\@totalleftmargin \advance\@tempdima -6\fboxsep \advance\@tempdima -2\fboxrule @@ -6654,6 +7969,11 @@ \leftskip=0pt \rightskip=0pt \vskip 2\fboxsep +% Change, 2016/05/09: We change \@totalleftmargin and \linewidth in +% case there are enumerate, itemize, or description environments +% inside the solution: + \@totalleftmargin=0pt + \linewidth=\hsize \solutiontitle \ignorespaces }% @@ -6673,7 +7993,8 @@ % \parshape) shift us to the right when we enter horizontal % mode. If we don't use this \hbox, then we'd have to comment % out the \hskip \@totalleftmargin: - \hbox to \textwidth{% + % 2016/02/08: Changed \textwidth to \hsize: + \hbox to \hsize{% \noindent \hskip\@totalleftmargin \hskip3\fboxsep\hskip\fboxrule @@ -6682,14 +8003,40 @@ \par\nointerlineskip \fi \endgroup % Finish confining the \Solution@Emphasis - \makeemptybox{\solutionbox@size}% - } +% Starting in version 2.502, 2016/03/23,the decision of whether to +% color the box is made in the \makeemptybox command: + \makeemptybox{\solutionbox@size} + }% End of the second argument of \newenvironment{solutionbox} + +%-------------------------------------------------------------------- +%-------------------------------------------------------------------- +% Added in version 2.502: 2016/03/23, \colorfbox +% The \colorfbox command is used in our modification of framed.sty +% that allows us to print the frame around the solution in color when +% the user has given the command \colorsolutionboxes. It takes two +% arguments, the first being the color for the frame, and the second +% being the stuff to be framed. +% If we had assumed that color.sty was used (instead of just color.sty), +% then the line that saves the current color in saved@color could have +% been just +% \colorlet{saved@color}{.} +% but we wanted to make this work even if color.sty is being used. +% When you define a color mycolor using either color.sty or +% xcolor.sty, a macro \csname\string\color@ mycolor\endcsname is +% defined (i.e., the macro name is \\color@mycolor). +\newcommand{\colorfbox}[2]{% + % Save the current color in saved@color: + \expandafter\let\csname\string\color@saved@color\endcsname\current@color + % Create the box in color #1, with the text in saved@color + % (the braces are to confine the color change commands): + {\color{#1}\fbox{\color{saved@color}#2}}% +}% colorfbox %-------------------------------------------------------------------- %-------------------------------------------------------------------- @@ -6945,7 +8292,19 @@ \catcode`\|=\FrameRestore % Provide configuration commands: -\providecommand\FrameCommand{\fboxrule=\FrameRule \fboxsep=\FrameSep \fbox} +%psh: Version 2.502, 2016/03/23, changed \FrameCommand so that the +% frame is printed in color if the user has said +% \colorsolutionboxes: +%\providecommand\FrameCommand{\fboxrule=\FrameRule \fboxsep=\FrameSep \fbox} +\def\FrameCommand{\fboxrule=\FrameRule \fboxsep=\FrameSep + \if@colorsolutionboxes + \def\box@it{\colorfbox{SolutionBoxColor}}% + \else + \def\box@it{\fbox}% + \fi + \box@it +}% \FrameCommand + \@ifundefined{FrameRule}{\newdimen\FrameRule \FrameRule=\fboxrule}{} \@ifundefined{FrameSep} {\newdimen\FrameSep \FrameSep =3\fboxsep}{} |