summaryrefslogtreecommitdiff
path: root/texmf-dist/source/latex/acrotex/dljslib.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'texmf-dist/source/latex/acrotex/dljslib.dtx')
-rw-r--r--texmf-dist/source/latex/acrotex/dljslib.dtx2692
1 files changed, 2692 insertions, 0 deletions
diff --git a/texmf-dist/source/latex/acrotex/dljslib.dtx b/texmf-dist/source/latex/acrotex/dljslib.dtx
new file mode 100644
index 00000000..cb974275
--- /dev/null
+++ b/texmf-dist/source/latex/acrotex/dljslib.dtx
@@ -0,0 +1,2692 @@
+% \iffalse
+%<*copyright>
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% dljsLib.sty package, %%
+%% Copyright (C) 2001-2016 D. P. Story %%
+%% dpstory@uakron.edu %%
+%% %%
+%% This program can redistributed and/or modified under %%
+%% the terms of the LaTeX Projet Public License %%
+%% Distributed from CTAN archives in directory %%
+%% macros/latex/base/lppl.txt; either version 1 of the %%
+%% License, or (at your option) any later version. %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%</copyright>
+%<package>\NeedsTeXFormat{LaTeX2e}
+%<package>\ProvidesPackage{dljslib}
+%<package> [2014/10/05 v2.0 Manage a Library of Document Level JavaScripts (dps)]
+%<*driver>
+\documentclass{ltxdoc}
+\usepackage[colorlinks,hyperindex=false]{hyperref}
+\pdfstringdefDisableCommands{\let\\\textbackslash}%
+%\OnlyDescription % comment out for implementation details
+\EnableCrossrefs
+\CodelineIndex
+\RecordChanges
+\InputIfFileExists{aebdocfmt.def}{\PackageInfo{dljslib}{Inputting aebdocfmt.def}}
+ {\def\IndexOpt{\DescribeMacro}\def\IndexKey{\DescribeMacro}\let\setupFullwidth\relax
+ \PackageInfo{dljslib}{aebdocfmt.def cannot be found}}
+\begin{document}
+ \GetFileInfo{dljslib.sty}
+ \title{The \texttt{dljsLib} Package}
+ \author{D. P. Story\\
+ Email: \texttt{dpstory@uakron.edu}}
+ \date{processed \today}
+ \maketitle
+ \tableofcontents
+ \let\Email\texttt
+ \DocInput{dljslib.dtx}
+\IfFileExists{\jobname.ind}{\newpage\setupFullwidth\PrintIndex}{\paragraph*{Index} The index goes here.\\Execute
+ \texttt{makeindex -s gind.ist -o dljslib.ind dljslib.idx} on the command line and recompile
+ \texttt{dljslib.dtx}.}
+\IfFileExists{\jobname.gls}{\PrintChanges}{\paragraph*{Change History} The list of changes goes here.\\Execute
+ \texttt{makeindex -s gglo.ist -o dljslib.gls dljslib.glo} on the command line and recompile
+ \texttt{dljslib.dtx}.}
+\end{document}
+%</driver>
+% \fi
+%
+% \InputIfFileExists{aebdonotindex.def}{\PackageInfo{dljslib}{Inputting aebdonotindex.def}}
+% {\PackageInfo{dljslib}{aebdonotindex.def cannot be found}}
+%
+% \changes{v1.3}{2005/08/27}{Added new user contributed routines for unordered,
+% interval, point and factored responses. Added a feature whereby you
+% can create a file called libcusopt.opt to declare your own options
+% for this package, but these should be a combination of existing
+% options.}
+% \changes{v2.0}{2014/10/05}{replaced app.alert with eqAppAlert}
+%
+%\section{Introduction}
+% This is a companion package to the \texttt{insdljs} package. \texttt{Insdljs} gives the
+% document author the ability to write document level JavaScripts to the PDF docuement, when
+% it is finally built. I personally will be writing a number of general routines to
+% handle situations that arise, and I would hope that other enthusiasts of \texttt{insdljs} will
+% do the same. Therefore, it is desirable to gather together some general purpose routines into
+% a single library, and have a way of selecting the desired function or functions to be used.
+%
+% This package is meant for document authors, not for package developers. This package can only be used
+% once per document, perhaps called from the preamble of a document.
+%
+%\section{The Library Procedures}
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+% Define some convenience commands, \cs{dljsRegister} and \cs{DeclareAndRegister}, see comments that follow.
+% \begin{macrocode}
+\newcommand\dljsRegister[2][n]
+ {\expandafter\let\csname checkout@#2\endcsname=#1}
+\def\@ifcheckedout#1{\expandafter\if\csname checkout@#1\endcsname y}
+\newcommand\DeclareAndRegister[1]
+ {\DeclareOption{#1}{\dljsRegister[y]{#1}}\dljsRegister{#1}}
+% \end{macrocode}
+% Here we ``register'' the functions contained in the library. \cs{dljsRegister} defines a
+% control sequence that records the name of the function (actually, the option name). This
+% control is set to `n'. When a package user chooses a particular function for inclusion, the
+% control is set to `y'.
+% \begin{macrocode}
+\dljsRegister[y]{dljslib}
+% \end{macrocode}
+% \subsection{Library Card Catalog}
+% \IndexOpt{equations}
+% \IndexOpt{vectors}
+% \IndexOpt{indefIntegral}
+% \IndexOpt{ImplMulti}
+% \IndexOpt{nodec}
+% The arguments of the \cs{DeclareAndRegister} command are the options of this package, and
+% their names represent JavaScript functions in the library.
+% \begin{macrocode}
+\DeclareAndRegister{equations}
+\DeclareAndRegister{vectors}
+\DeclareAndRegister{indefIntegral}
+\DeclareAndRegister{ImplMulti}
+\DeclareAndRegister{nodec}
+% \end{macrocode}
+% \IndexOpt{noBinFac}
+% \IndexOpt{limitArith}
+% \IndexOpt{combinatorics}
+% \IndexOpt{setSupport}
+% More options.
+% \begin{macrocode}
+\DeclareAndRegister{noBinFac}
+\DeclareAndRegister{limitArith}
+\DeclareAndRegister{combinatorics}
+\DeclareAndRegister{setSupport}
+% \end{macrocode}
+% \IndexOpt{unordered}
+% \IndexOpt{complex}
+% \IndexOpt{satisfyEq}
+% \IndexOpt{useGermanNums}
+% \IndexOpt{factors}
+% More options still.
+% \begin{macrocode}
+\DeclareOption{unordered}{\PackageWarning{dljslib}
+ {The `unordered' option is now combined with the\MessageBreak
+ `setSupport' option, will use the `setSupport' option\MessageBreak
+ instead}\ExecuteOptions{setSupport}}
+%\DeclareAndRegister{unordered}
+\DeclareAndRegister{complex}
+\DeclareAndRegister{satisfyEq}
+\DeclareAndRegister{useGermanNums}
+\DeclareAndRegister{factors}
+% \end{macrocode}
+% \IndexOpt{point}
+% \IndexOpt{intervals}
+% The final set of options.
+% \begin{macrocode}
+\DeclareAndRegister{point}
+\DeclareAndRegister{intervals}
+\def\includeOptions#1{\@for\@option:=#1\do{\dljsRegister[y]{\@option}}}
+\InputIfFileExists{libcusopt.opt}{}{}
+% \end{macrocode}
+% \begin{macrocode}
+\ProcessOptions
+% \end{macrocode}
+% \subsection{Requirements for a Library Card}
+%
+% In order to check a function out of this library, you must have
+% the \texttt{insdljs} Package. The \texttt{insdljs} package itself
+% has software requirements: (1) the \texttt{verbatim} and
+% \texttt{hyperref} packages; (2) One of the following,
+% \textsf{Distiller~5.0} or greater, \textsf{pdftex}, or
+% \textsf{dvipdfm}.
+%
+% \begin{macrocode}
+\RequirePackage{insdljs}
+% \end{macrocode}
+% After inputting \textsf{insdljs}, we define |\setdecimalpoint|, which populates
+% the text command |\aebdecimalpoint|. The text command |\aebdecimalpoint| is used in the \texttt{nodec}
+% option.
+% \begin{macrocode}
+\def\setdecimalpoint#1{\def\aebdecimalpoint{\eqbs#1}}
+\setdecimalpoint{.}
+% \end{macrocode}
+%
+% \subsection{Checkout Procedure}
+%
+% Functions can be checkout of the library by using the \texttt{dljslib} package
+% in the usual way. For example:
+%\begin{verbatim}
+%\documentclass{article}
+%\usepackage{amsmath}
+%\usepackage[pdftex,designi]{web}
+%\usepackage{exerquiz}
+%\usepackage[indefIntegral]{dljslib} % <- check out `indefIntegral'
+%\end{verbatim}
+% Here, we first use \texttt{exerquiz}, which has \texttt{insdljs} as a required package.
+% The use of this package is not limited to users of \texttt{exerquiz}, for example, we can
+% say
+%\begin{verbatim}
+%\documentclass{article}
+%\def\mydriver{dvipdfm}
+%\usepackage[\mydriver]{color}
+%\usepackage[\mydriver,colorlinks,pdfpagemode=None]{hyperref}
+%\usepackage[\mydriver]{insdljs}
+%\usepackage[indefIntegral]{dljslib} % <- check out `indefIntegral'
+%\end{verbatim}
+% However, at the time of the release of v1.0 of this package, the only functions in this
+% library are ones used by \texttt{exerquiz}.
+%
+% \subsection{Exiting the Library with your Checkouts}
+%
+% This package has an output stream, \cs{dljslib@verbatim@out},
+% that is used to write all the functions that are to be included.
+% We use a control sequence \cs{js@verbatim@out} defined in
+% \texttt{insdljs}. We also use a verbatim write from
+% \texttt{insdljs} as well, the \cs{js@verbatimwrite} environment.
+% \cs{js@verbatimwrite} writes to the output stream pointed to
+% \cs{js@verbatim@out}.
+% \begin{macrocode}
+\newwrite\dljslib@verbatim@out
+% \end{macrocode}
+% This package generates only one auxiliary file,
+% \texttt{dljslib.ljs}, which can be deleted after the document is
+% latexed. The file \texttt{dljslib.ljs} (\texttt{ljs} means
+% ``library javascripts'') and contains the functions that are
+% specified in the package options. At the end of this package,
+% the file \texttt{dljslib.ljs} is input back into the calling
+% document where the package \texttt{insdljs} takes over.
+% \begin{macrocode}
+\immediate\openout\dljslib@verbatim@out=dljslib.ljs
+% \end{macrocode}
+% \subsection{The Catalog and Checkout Mechanism}
+% \begin{macro}{library@holding}
+% This is a simple environment, it reads its parameter, and if that option was specified by the
+% user, it writes the function verbatim to the \texttt{dljslib.ljs}, otherwise, it comments out
+% that function using the \texttt{comment} environment from the \texttt{verbatim} package.
+% This environment uses, \texttt{js@verbatimwrite}, an environment
+% defined in the \texttt{insdljs} package.
+% \begin{macrocode}
+\newenvironment{library@holding}[1]
+{%
+ \expandafter\ifx\csname checkout@#1\endcsname y%
+ \let\js@verbatim@out\dljslib@verbatim@out
+ \let\dljs@verbatim\js@verbatimwrite
+ \let\enddljs@verbatim\endjs@verbatimwrite\else
+ \let\dljs@verbatim\comment
+ \let\enddljs@verbatim\endcomment\fi\dljs@verbatim
+}{\enddljs@verbatim}
+% \end{macrocode}
+% \end{macro}
+%\section{The DLJS Library}
+% We finally reach the ``stacks'', the location of the actual library holdings.
+%
+% \medskip\noindent This item must always accompany the collection of functions that are to be
+% checked out. This is the beginning of the \texttt{insDLJS} environment. To continue this library analogy,
+% this of this as the ``front wrapper'' or ``front cover'' of your library selections. It contains the
+% ``name'' of the library from which you checked out your selections.
+% \begin{macrocode}
+\begin{library@holding}{dljslib}
+\begin{insDLJS*}[dljslib]{dljslib}
+\begin{newsegment}{dljslib: AcroTeX DLJS Library}
+/*
+ The Document Level JavaScript Library
+ D. P. Story copyright 2001-\the\year
+*/
+var dljslib = true;
+\end{newsegment}
+\end{library@holding}
+% \end{macrocode}
+%\subsection{The Stacks}
+% Now we reach of the beginning of the stacks. There are several sections of the library, currently,
+% \nameref{s:respfunctions} and \nameref{s:compfunctions}.
+%
+% \subsection{Response Functions}\label{s:respfunctions}
+%
+% \textbf{Used by Exerquiz.} In this section we catalog response functions. A response function is the one that
+% \cs{RespBoxMath} calls to process the user's response to a math fill-in question. See the sample file
+% \texttt{jqzspec.tex} for a detailed explanation of this type of function.
+%
+% \subsubsection{\texttt{equations}}\label{equations}
+% \IndexOpt{equations}
+% These routines process questions for which an equation is the expected answer.
+% \begin{macrocode}
+\@ifcheckedout{equations}
+\newcommand\equationsAlertMsg{"An equation is expected"}
+\fi
+\begin{library@holding}{equations}
+\begin{newsegment}{dljslib: Equation Handling}
+function ProcRespEq(flag,CorrAns,n,epsilon,domain,indepVar,oComp)
+{
+ if (!ProcessIt) return null;
+ ok2Continue = true;
+ var success;
+ var fieldname = event.target.name;
+ var UserAns = event.value;
+% \end{macrocode}
+% \begin{macrocode}
+ var CorrExpressions = CorrAns.split("=");
+ var zCorrAns = "("+CorrExpressions[0]+")-("+CorrExpressions[1] +")";
+ UserAns = stripWhiteSpace (UserAns);
+ if(!ok2Continue ) return null;
+ if (!/[=]/.test(UserAns)) {
+ eqAppAlert(\equationsAlertMsg, 3);
+ return null;
+ }
+% \end{macrocode}
+% Check for commas, not permitted here. (2012/05/25)
+% \begin{macrocode}
+ var reComma=/,/;
+ if ( reComma.test(UserAns) ) {
+ eqAppAlert(\eqSyntaxErrorComma,3);
+ return null;
+ }
+% \end{macrocode}
+% \begin{macrocode}
+ var UserExpressions = UserAns.split("=");
+ var zUserAns = "("+UserExpressions[0]+")-("+UserExpressions[1] +")";
+
+% \end{macrocode}
+% If \texttt{oComp} is an object, then see if it has a \texttt{comp} property
+% \begin{macrocode}
+ var comp = ( typeof oComp == "object" ) ?
+ (typeof oComp.comp == "undefined" ) ?
+ diffCompare : oComp.comp : oComp;
+% \end{macrocode}
+% The \texttt{comp} parameter, which has been changed to \texttt{oComp} can now be
+% an object. One property of this object is \texttt{comp}, handled above. Another
+% property is \texttt{priorParse}, this is a function that returns \texttt{null}
+% or \texttt{true}. This \texttt{priorParse} function allows for additional
+% filtering of the \texttt{zUserAns} before parsing. If it returns \texttt{true},
+% we are ok to continue, if \texttt{null}, we don't like something the user has entered,
+% and ask him/her to change it.
+% \begin{macrocode}
+ if ( typeof oComp == "object"
+ && typeof oComp.priorParse != "undefined" ) {
+% \end{macrocode}
+% Let's go ahead and allow \texttt{oComp.priorParse} be an array of functions.
+% \begin{macrocode}
+ if ( typeof oComp.priorParse == "object" ) {
+ for ( var i=0; i < oComp.priorParse.length; i++) {
+ var retn = oComp.priorParse[i](zUserAns);
+ if ( retn == null ) return null;
+ }
+ } else {
+ var retn = oComp.priorParse(zUserAns);
+ if ( retn == null ) return null;
+ }
+ }
+ zCorrAns = ParseInput(zCorrAns);
+ if (!ok2Continue) {
+ eqAppAlert("Syntax error in author's answer! Check console.", 3);
+ console.println("Syntax Error: " + CorrAns);
+ return null;
+ }
+ zUserAns = ParseInput(zUserAns);
+ if (!ok2Continue) return null;
+% convert vars to new format, if needed
+ indepVar = TypeParameters(indepVar);
+ var lambda = getNonZeroRatio (domain, indepVar, zCorrAns, zUserAns);
+ if ( lambda == null ) {
+ eqAppAlert(\eqSyntaxErrorUndefVar,3); return null; };
+ if ( !ok2Continue ) return notifyField(false, flag, fieldname);
+ zCorrAns = lambda + "*(" + zCorrAns + ")";
+ success=randomPointCompare (n,domain,indepVar,epsilon,
+ zCorrAns,zUserAns,comp)
+ if ( success == null ) { eqAppAlert(\eqSyntaxErrorUndefVar,3);
+ return null; }
+ return notifyField(success, flag, fieldname);
+}
+function getNonZeroRatio (_a, _v, _F, _G)
+{
+ var _i, _j;
+ var aXY = new Array();
+ _a = _a.replace(/[\[\]\s]/g, "");
+ var _V = _v.split(","); // e.g. _V[0] = "i:x"
+ var _n = _V.length;
+ var aIntervals = _a.split("&");
+ var aInterval = aIntervals[0].split("x");
+ var endpoints = aInterval[0].split(",");
+ for (_j=0; _j < 4; _j++) {
+ for (_i = 0; _i < _n; _i++) {
+ var endpoints = aInterval[_i].split(",");
+ aXY[_i] = endpoints[0]-0
+ +(endpoints[1]-endpoints[0])*Math.random();
+\db console.println("aXY["+_i+"] = " + aXY[_i]);\db%
+ }
+ for (var _i = 0; _i< _n; _i++) {
+ if (_V[_i].charAt(0) == "r" )
+ eval ( "var "+ _V[_i].charAt(2)
+ + " = " + aXY[_i] + ";");
+ else // assume type "i"
+ eval ( "var "+ _V[_i].charAt(2)
+ + " = " + Math.ceil(aXY[_i]) + ";");
+ }
+ _F = eval(_F);
+ if ( app.viewerVersion >= 5)
+ {
+ var rtnCode = 0;
+ eval("try { if(isNaN(_G = eval(_G))) rtnCode=-1; }"
+ +"catch (e) { rtnCode=1; }");
+ switch(rtnCode) {
+ case 0: break;
+ case 1: return null;
+ case -1: ok2Continue=false;
+ return -1;
+ }
+ }
+ else
+ if(isNaN(_G=eval(_G))) {ok2Continue=false;return -1;}
+ if ( _F != 0 && _G != 0 ) return _G/_F;
+ }
+ console.println( "Can't find a non zero scalar");
+ return null;
+}
+\end{newsegment}
+\end{library@holding}
+% \end{macrocode}
+%
+% \subsubsection{\texttt{vectors}}\label{vectors}
+%
+% \IndexOpt{vectors}
+% This function attempts to process questions that have vectors
+% as answers. Note the name of the function is \texttt{ProcVec}, this is the name used to call it.
+%\begin{verbatim}
+%$\vec a + \vec b = \RespBoxMath{<4, 4, 4>}{1}{.0001}{[2,4]}*{ProcVec}$
+%\end{verbatim}
+%See also the file \texttt{jqzspec.tex} for more details.
+% \begin{macrocode}
+\@ifcheckedout{vectors}
+\newcommand\vectorsErrorMsgi{"I'm looking for a vector.
+ You need to use proper vector notation, try using
+ angle brackets <....>."}
+\newcommand\vectorsErrorMsgii{"Angle brackets are not balanced.
+ Check the expression you typed in."}
+\newcommand\vectorsErrorMsgiii{"Incorrect number of components.
+ The answer requires " + aCorrAns.length+" components."}
+\def\vectorEmptyCompMsgiv(#1){"You entered nothing for the
+ component " +(#1+1) +" of your answer. Please enter
+ a component for the vector."}
+\fi
+\begin{library@holding}{vectors}
+\begin{newsegment}{dljslib: Vector Handling}
+function ProcVec (flag,CorrAns,n,epsilon,domain,indepVar,oComp)
+{
+% \end{macrocode}
+% This function attempts to process questions that have vectors
+% as answers.
+% \begin{macrocode}
+ if (!ProcessIt) return null;
+ ok2Continue = true;
+ var i, success, truthCnt=1;
+ var aScalar, scalar = 1;
+ var fieldname = event.target.name;
+ var UserAns = event.value;
+ UserAns = stripWhiteSpace(UserAns); // sets ok2Continue
+ CorrAns = stripWhiteSpace(CorrAns);
+ if ( !ok2Continue ) return null;
+ var isSpecResp=false;
+ for ( var i=0; i<aDlLibSpecResp.length; i++) {
+ if (CorrAns == aDlLibSpecResp[i]) {
+ isSpecResp=true; break;
+ }
+ }
+ for ( var i=0; i<aDlLibSpecResp.length; i++) {
+ if (UserAns == aDlLibSpecResp[i]) {
+ success = (CorrAns == UserAns);
+ return notifyField(success, flag, fieldname);
+ }
+ }
+ if (isSpecResp) return notifyField(false, flag, fieldname);
+ if (!/[<>]/.test(UserAns)) {
+ eqAppAlert(\vectorsErrorMsgi, 3);
+ return null;
+ }
+ if (!CkBalP(UserAns,"<",">")) {
+ eqAppAlert(\vectorsErrorMsgii, 3);
+ return null;
+ }
+ // see if there is a scalar multiple to the left of '<'
+ aScalar = UserAns.match(/(.*)(\*)(\s*<)/);
+ if (aScalar != null) {
+ scalar = aScalar[1];
+ UserAns = UserAns.slice(aScalar.index + aScalar[0].length-1)
+ }
+% \end{macrocode}
+% If \texttt{oComp} is an object, then see if it has a \texttt{comp} property
+% \begin{macrocode}
+ var comp = ( typeof oComp == "object" ) ?
+ (typeof oComp.comp == "undefined" ) ?
+ diffCompare : oComp.comp : oComp;
+% \end{macrocode}
+% \begin{macrocode}
+ CorrAns = CorrAns.replace(/[<>]/g, ""); // strip of < and >
+ UserAns = UserAns.replace(/[<>]/g, "");
+% \end{macrocode}
+% The \texttt{comp} parameter, which has been changed to \texttt{oComp} can now be
+% an object. One property of this object is \texttt{comp}, handled above. Another
+% property is \texttt{priorParse}, this is a function that returns \texttt{null}
+% or \texttt{true}. This \texttt{priorParse} function allows for additional
+% filtering of the \texttt{UserAns} before parsing. If it returns \texttt{true},
+% we are ok to continue, if \texttt{null}, we don't like something the user has entered,
+% and ask him/her to change it.
+% \begin{macrocode}
+ if ( typeof oComp == "object" && %
+typeof oComp.priorParse != "undefined" ) {
+ var retn=processSpecialParse(oComp.priorParse,UserAns);
+ if (retn==null) return null;
+ }
+% \end{macrocode}
+% Not convert each to an array
+% \begin{macrocode}
+ aUserAns = UserAns.split(",");
+ aCorrAns = CorrAns.split(",");
+ if (scalar != 1)
+ for (i=0; i<aUserAns.length; i++)
+ aUserAns[i]=""+scalar+"*"+aUserAns[i];
+ if (aCorrAns.length != aUserAns.length) {
+ eqAppAlert(\vectorsErrorMsgiii,3);
+ return null;
+ }
+ // convert to new format, if needed
+ indepVar = TypeParameters(indepVar);
+ for (i=0; i<aCorrAns.length; i++) {
+ aCorrAns[i] = ParseInput(aCorrAns[i]);
+ if (!ok2Continue) {
+ eqAppAlert("Author Syntax error!", 3);
+ return null;
+ }
+ aUserAns[i] = ParseInput(aUserAns[i]);
+ if (aUserAns[i]==null) {
+ eqAppAlert(\vectorEmptyCompMsgiv(i), 3);
+ return null;
+ }
+ if (!ok2Continue) return null;
+ success=randomPointCompare (n,domain,indepVar,epsilon,
+ aCorrAns[i],aUserAns[i],comp)
+ if ( success == null ) {
+ eqAppAlert(\eqSyntaxErrorUndefVar,3); return null; }
+ truthCnt *= (success) ? 1 : 0;
+ }
+ return notifyField(!!truthCnt, flag, fieldname);
+}
+\end{newsegment}
+\end{library@holding}
+% \end{macrocode}
+% \subsubsection{\texttt{setSupport}}
+%
+% \IndexOpt{setSupport}
+% We introduce two JavaScript functions \texttt{ProcRespSetNum} and
+% \texttt{ProcRespSetSym}, for handling a set of answers or a comma
+% delimited list of answers. The first function is for
+% numerical answers, the second for simple symbolic answers. The
+% demo file for these two response functions is
+% \texttt{set\_test.tex}; the examples below were taken from that file.
+%\changes{v1.9h}{2014/01/21}{Combined unordered option with setSupport option}
+% \begin{macrocode}
+\@ifcheckedout{setSupport}
+\newcommand{\noBracesInAnsMsg}{"Do not insert braces in your answer.
+ Please remove the braces (\{\})."}
+\newcommand{\noBracketsInAnsMsg}{"Do not insert braces in your answer.
+ Please remove the angle brackets (<>)."}
+\fi
+\begin{library@holding}{setSupport}
+\begin{newsegment}{dljslib: Support for Sets}
+% \end{macrocode}
+% This function can handle (math fill-in) questions whose answers are a set of numbers or a
+% comma delimited list of numbers.
+%\begin{flushleft}
+% Sample usage:
+%\begin{verbatim}
+% $(x+1)(2x-1)(x-2)^3 = 0$, $x = \RespBoxMath[\rectW{.75in}\textSize{0}]
+% {-1, 1/2, 2, 2, 2}{1}{.0001}{[0,1]}*{ProcRespSetNum}$
+%\end{verbatim}
+%\end{flushleft}
+% \begin{macrocode}
+function ProcRespSetNum(flag,CorrAns,n,epsilon,domain,indepVar,oComp)
+{
+ ok2Continue = true;
+ if (!ProcessIt) return null;
+ var fieldname = event.target.name;
+ var UserAns = event.value;
+ UserAns = stripWhiteSpace(UserAns);
+ CorrAns = stripWhiteSpace(CorrAns);
+ if (!ok2Continue) return null;
+ var isSpecResp=false;
+ for ( var i=0; i<aDlLibSpecResp.length; i++) {
+ if (CorrAns == aDlLibSpecResp[i]) {
+ isSpecResp=true; break;
+ }
+ }
+ for ( var i=0; i<aDlLibSpecResp.length; i++) {
+ if (UserAns == aDlLibSpecResp[i]) {
+ success = (CorrAns == UserAns);
+ return notifyField(success, flag, fieldname);
+ }
+ }
+ if (isSpecResp) return notifyField(false, flag, fieldname);
+ var retn=ck4Braces(UserAns);
+ if (retn==null) return null;
+ var aUserAns = UserAns.split(",");
+% \end{macrocode}
+% Make sure all of UserAns are numbers
+% \begin{macrocode}
+ for ( var i=0; i < aUserAns.length; i++) {
+ try {
+ if (isNaN(eval(aUserAns[i]))) return syntaxError(), null;
+ } catch(e) { return syntaxError(), null; }
+ }
+ var aUserAns = aUserAns.sort(
+ function(a,b) { return eval(a) - eval(b);} );
+ var aCorrAns = CorrAns.split(",").sort(
+ function(a,b) { return eval(a) - eval(b);} );
+ var numCorrect = 0;
+ if ( aUserAns.length != aCorrAns.length )
+ return notifyField(false, flag, fieldname);
+ for ( var i=0; i< aCorrAns.length; i++) {
+ var retn = _ProcResp(%
+flag,aCorrAns[i],aUserAns[i],n,epsilon,domain,indepVar,oComp);
+ if ( retn == -1 ) return null;
+ if ( retn == null ) return syntaxError(), null;
+ numCorrect += (retn) ? 1 : 0;
+ }
+ var success = (numCorrect == aCorrAns.length);
+ return notifyField(success, flag, fieldname);
+}
+var ok2format=true;
+function formatAsSet() {
+ if (ok2format&&event.value.replace(/\\s/g,"") != "")
+ event.value = "{ " + event.value + " }";
+}
+function ck4Braces (UserAns) {
+ ok2format=true;
+ if (/^\{/.test(UserAns) || /\}$/.test(UserAns) ) {
+ ok2format=false;
+ return eqAppAlert(\noBracesInAnsMsg,3), null;
+ }
+ else return true;
+}
+function ck4AngleBrackets (UserAns) {
+ ok2format=true;
+ if (/^</.test(UserAns) || />$/.test(UserAns) ) {
+ ok2format=false;
+ return eqAppAlert(\noBracketsInAnsMsg,3), null;
+ }
+ else return true;
+}
+function formatAsVector() {
+ if (ok2format&&event.value.replace(/\\s/g,"") != "") {
+ event.value = "< " + event.value + " >";
+ }
+}
+% \end{macrocode}
+% Input an order pair of numbers, determine if it satisfies an equation. This problem
+% type is new as there are ``infinitely many answers.'' If we pose the question
+% ``Enter a point that lies on the line $2x+3y=6$.'' The user enters an ordered pair
+% of numbers. The CorrAns that is passed is $2x+3y-6$, and we test the user's input
+% to see if the expression evaluates to zero.
+%
+% \begin{macrocode}
+% \end{macrocode}
+% This function can handle simple symbolic answers. The variable list, \texttt{inderVar}, should
+% be the ``universal set'' of the problem.
+% \begin{flushleft}
+% Sample usage:
+%\begin{verbatim}
+% \def\U{a,b,c,d,e,f,g} % define a universal set
+% $A \cap B = \RespBoxMath[\AddAAFormat{\formatAsSet}
+% \rectW{.75in}\textSize{0}
+% ]{c,d}(\U){1}{.0001}{[0,1]}*{ProcRespSetSym}$
+%\end{verbatim}
+% \end{flushleft}
+% \begin{macrocode}
+function ProcRespSetSym(flag,CorrAns,n,epsilon,domain,indepVar,oComp)
+{
+ ok2Continue = true;
+ if (!ProcessIt) return null;
+ var fieldname = event.target.name;
+ var UserAns = event.value;
+ UserAns = stripWhiteSpace(UserAns);
+ CorrAns = stripWhiteSpace(CorrAns);
+ if (!ok2Continue) return null;
+ var isSpecResp=false;
+ for ( var i=0; i<aDlLibSpecResp.length; i++) {
+ if (CorrAns == aDlLibSpecResp[i]) {
+ isSpecResp=true; break;
+ }
+ }
+ for ( var i=0; i<aDlLibSpecResp.length; i++) {
+ if (UserAns == aDlLibSpecResp[i]) {
+ success = (CorrAns == UserAns);
+ return notifyField(success, flag, fieldname);
+ }
+ }
+ if (isSpecResp) return notifyField(false, flag, fieldname);
+ var retn=ck4Braces(UserAns);
+ if (retn==null) return null;
+ var aUserAns = UserAns.split(",");
+ var _V = indepVar.split(",");
+ for ( var _i=0; _i < _V.length; _i++) {
+ eval ( "var "+ _V[_i] + " = \"" + _i + "\";");
+ }
+% \end{macrocode}
+% Make sure all of UserAns are numbers
+% \begin{macrocode}
+ for ( var _i=0; _i < aUserAns.length; _i++) {
+ try {
+ if (isNaN(eval(aUserAns[_i]))) return syntaxError(), null;
+ aUserAns[_i] = eval(aUserAns[_i]);
+ } catch(e) { return syntaxError(), null; }
+ }
+ var aCorrAns = CorrAns.split(",");
+ for ( var _i=0; _i < aCorrAns.length; _i++) {
+ try {
+ if (isNaN(eval(aCorrAns[_i])))
+ return eqAppAlert(
+ "Author error, recheck your code",3), null;
+ aCorrAns[_i] = eval(aCorrAns[_i]);
+ } catch(e) { return eqAppAlert(
+ "Author error, recheck your code",3), null; }
+ }
+ var aUserAns = aUserAns.sort(
+ function(a,b) { return eval(a) - eval(b);} );
+ var aCorrAns = aCorrAns.sort(
+ function(a,b) { return eval(a) - eval(b);} );
+ var numCorrect = 0;
+ if ( aUserAns.length != aCorrAns.length )
+ return notifyField(false, flag, fieldname);
+ for ( var _i=0; _i< aCorrAns.length; _i++) {
+ var retn = _ProcResp(%
+flag,aCorrAns[_i],aUserAns[_i],n,epsilon,"[0,1]","i:_x",oComp);
+ if ( retn == -1 ) return null;
+ if ( retn == null ) return syntaxError(), null;
+ numCorrect += (retn) ? 1 : 0;
+ }
+% console.println( aUserAns.toSource() );
+ var success = (numCorrect == aCorrAns.length);
+% var success = (numCorrect == aCorrAns.length) ? true : false;
+% if ( success == null ) return syntaxError(), null;
+ return notifyField(success, flag, fieldname);
+}
+% \end{macrocode}
+% The function \texttt{ProcRespListFormula} can be used to process a comma-delimited
+% \emph{ordered} list of expressions.
+% \begin{macrocode}
+function ProcRespListFormula(flag,CorrAns,n,epsilon,domain,%
+indepVar,oComp) {
+ ok2Continue = true;
+ if (!ProcessIt) return null;
+ var fieldname = event.target.name;
+ var UserAns = event.value;
+ UserAns = stripWhiteSpace(UserAns);
+ CorrAns = stripWhiteSpace(CorrAns);
+ if (!ok2Continue) return null;
+ var isSpecResp=false;
+ for ( var i=0; i<aDlLibSpecResp.length; i++) {
+ if (CorrAns == aDlLibSpecResp[i]) {
+ isSpecResp=true; break;
+ }
+ }
+ for ( var i=0; i<aDlLibSpecResp.length; i++) {
+ if (UserAns == aDlLibSpecResp[i]) {
+ success = (CorrAns == UserAns);
+ return notifyField(success, flag, fieldname);
+ }
+ }
+ if (isSpecResp) return notifyField(false, flag, fieldname);
+ var retn=ck4AngleBrackets(UserAns);
+ if (retn==null) return null;
+ UserAns = UserAns.replace(/,+/g, ",");
+ UserAns = UserAns.replace(/,$/, "");
+ UserAns = UserAns.replace(/^,/, "");
+ event.value = UserAns;
+ CorrAns = stripWhiteSpace(CorrAns);
+ if (!ok2Continue) return null;
+ var aUserAns = UserAns.split(",");
+ var aCorrAns = CorrAns.split(",");
+ var numCorrect = 0;
+ if ( aUserAns.length != aCorrAns.length )
+ return notifyField(false, flag, fieldname);
+ for ( var i=0; i< aCorrAns.length; i++) {
+ var retn =_ProcResp(flag,aCorrAns[i],aUserAns[i],%
+n,epsilon,domain,indepVar,oComp);
+ if ( retn == -1 ) return null;
+ if ( retn == null ) return syntaxError(), null;
+ numCorrect += (retn) ? 1 : 0;
+ }
+ var success = (numCorrect == aCorrAns.length);
+ return notifyField(success, flag, fieldname);
+}
+% \end{macrocode}
+% The JS function \texttt{ProcRespSetFormula} will grade an
+% unordered list of formulas, such as
+% \texttt{x}, \verb!x^2!, \verb!x^3!. The code is a modification of
+% \texttt{ProcRespListFormula}. The idea is to split the user and correct
+% answers and then compare the first correct answer with each of the
+% user answers in turn. If there is a match, swap that user answer
+% with the first user answer. Then compare the second correct answer
+% with the rest of the user answers, and continue in that way.
+%\begin{flushleft}
+% Usage:
+%\begin{verbatim}
+%\def\formulasetbox#1#2#3{\RespBoxMath{#1}(#2)[\thequestionno]{10}
+% {1.0E-15}{#3}*{ProcRespSetFormula}}
+%\formulasetbox{x,x^2,x^3}{x}{[1,2]}
+%\end{verbatim}
+%\end{flushleft}
+% \begin{macrocode}
+function ProcRespSetFormula(flag,CorrAns,n,epsilon,%
+domain,indepVar,oComp) {
+ ok2Continue = true;
+ if (!ProcessIt) return null;
+ var fieldname = event.target.name;
+ var UserAns = event.value;
+ UserAns = stripWhiteSpace(UserAns);
+ CorrAns = stripWhiteSpace(CorrAns);
+ if (!ok2Continue) return null;
+ var isSpecResp=false;
+ for ( var i=0; i<aDlLibSpecResp.length; i++) {
+ if (CorrAns == aDlLibSpecResp[i]) {
+ isSpecResp=true; break;
+ }
+ }
+ for ( var i=0; i<aDlLibSpecResp.length; i++) {
+ if (UserAns == aDlLibSpecResp[i]) {
+ success = (CorrAns == UserAns);
+ return notifyField(success, flag, fieldname);
+ }
+ }
+ if (isSpecResp) return notifyField(false, flag, fieldname);
+ var retn=ck4Braces(UserAns);
+ if (retn==null) return null;
+ UserAns = UserAns.replace(/,+/g, ",");
+ UserAns = UserAns.replace(/,$/, "");
+ UserAns = UserAns.replace(/^,/, "");
+ event.value = UserAns;
+ CorrAns = stripWhiteSpace(CorrAns);
+ if (!ok2Continue) return null;
+ var aUserAns = UserAns.split(",");
+ var aCorrAns = CorrAns.split(",");
+ var numCorrect = 0, match = 0;
+ if ( aUserAns.length != aCorrAns.length )
+ return notifyField(false, flag, fieldname);
+ for ( var i=0; i<aCorrAns.length; i++) {
+ match = 0;
+ for ( var j=i; j< aUserAns.length; j++) {
+ var retn = _ProcResp(%
+flag,aCorrAns[i],aUserAns[j],n,epsilon,domain,indepVar,oComp);
+ if ( retn == -1 ) return null;
+ if ( retn == null ) return syntaxError(), null;
+ if (retn==1) {
+ var temp=aUserAns[j];
+ aUserAns[j]=aUserAns[i];
+ aUserAns[i]=temp;
+ match = match + 1;
+ }
+ }
+ numCorrect += (match) ? 1 : 0;
+ }
+ var success = (numCorrect == aCorrAns.length);
+ return notifyField(success, flag, fieldname);}
+\end{newsegment}
+\end{library@holding}
+% \end{macrocode}
+% \subsubsection{\texttt{complex}}
+% \IndexOpt{complex}The function \texttt{ProcRespComplex} supplies support for answers that are
+% complex numbers. The function test whether there is exactly one \texttt{i}
+% in the expression, if not, a message appears reminding the user that the form
+% of a complex number is \texttt{a + bi}. The function adds \texttt{i} as one of the
+% variables, and appends \texttt{x[0,1]} to the domain specification. This might be a
+% problem if the document author uses the old style of specifying the domain; use
+% \texttt{[a,b]} to specify the domain, we change this to \texttt{[a,b]x[0,1]}.
+%
+% The rest of the function is like a multiv-ariable problem. Seems to work ok.
+% Here is a sample syntax:
+%\begin{verbatim}
+% $(3+3i)(4+5i) = \RespBoxMath{-3+27i}{3}{0.0001}{[0,1]}*{ProcRespComplex}$
+%\end{verbatim}
+%The correct answer must be supplied with the \cs{RespBoxMath}, and is used to compare
+% with what the student enters.
+% \changes{v1.7}{2007/10/12}
+%{%
+% Added support for answer that are complex numbers.
+%}
+% \begin{macrocode}
+\@ifcheckedout{complex}
+\newcommand{\complexPowerAlertMsg}{%
+ "Powers of i (for example, i^2, i^3) are not supported,
+ replace powers of i with their complex equivalents."}
+\newcommand{\complexCisAlertMsg}{%
+ "The cis function does not support exponents. Write,
+ for example, cis^3(x) as cis(3*x), instead."}
+\newcommand{\alertNotComplexMsg}{%
+ "The expression is not in the form of a complex
+ number, a+bi"}
+\def\emptyCompComplexMsg(#1){%
+ "You entered nothing for the component "
+ +(#1+1)+" of your answer. Please enter a complex number."}
+\fi
+\begin{library@holding}{complex}
+\begin{newsegment}{dljslib: Support for Complex Numbers}
+% \end{macrocode}
+% Defined (11/11/08). The \texttt{cis} function is undefined unless the \texttt{complex} option is used.
+% \begin{macrocode}
+function cis(x,i) { return Math.cos(x) + i*Math.sin(x); }
+JSfCustom.push("cis");
+JSf = JSf.concat(JSfBuiltIn, JSfCustom);
+function ProcRespComplex(flag,CorrAns,n,epsilon,domain,indepVar,oComp)
+{
+ if (!ProcessIt) return null;
+ ok2Continue = true;
+ var i, success;
+ var fieldname = event.target.name;
+ var UserAns = event.value;
+ CorrAns = stripWhiteSpace (CorrAns);
+ UserAns = stripWhiteSpace (UserAns); // sets ok2Continue
+ if ( !ok2Continue ) return null;
+ indepVar+="i";
+ domain+="x[0,1]";
+% \end{macrocode}
+% We don't support powers of i
+% \begin{macrocode}
+ if (/(\b|[^a-zA-Z])i\^/.test(UserAns)) {
+ eqAppAlert(\complexPowerAlertMsg,3);
+ return null;
+ }
+% \end{macrocode}
+% Powers of \texttt{cis} function are not supported at this time.
+% \begin{macrocode}
+% var re=/cis\^/
+ if ( /cis\^/.test(UserAns) ) {
+ eqAppAlert(\complexCisAlertMsg,3);
+ return null;
+ }
+% \end{macrocode}
+% (2012/05/25) Check for commas, not permitted here.
+% \changes{v1.9f}{2012/11/17}{Moved this block higher, before testing for comma.}
+% \begin{macrocode}
+ var reComma=/,/;
+ if ( reComma.test(UserAns) ) {
+ eqAppAlert(\eqSyntaxErrorComma,3);
+ return null;
+ }
+% \end{macrocode}
+% To make the \texttt{cis} function work, we've defined it to be a function of two variables,
+% \texttt{cis(x,i)}, but the user does not need to know that. We search for \texttt{cis(<arg>)} and
+% replace with \texttt{cis(<arg>,i)}. I hope this works.
+% \begin{macrocode}
+ UserAns=changeArgs4Cis(UserAns);
+% \end{macrocode}
+% ...and do the same thing for the correct answer.
+% \begin{macrocode}
+ CorrAns=changeArgs4Cis(CorrAns);
+% \end{macrocode}
+% The complex option is not meant to be an option to do complex arithmetic, but only
+% to accept a complex number as an answer. So, we do not accept an answer with two
+% or more i's in it.
+% \begin{macrocode}
+ var aMatch = UserAns.match(/(\b|[^a-zA-Z])i/g);
+ if ( aMatch != null && aMatch.length > 1) {
+ eqAppAlert(\alertNotComplexMsg, 3);
+ return null;
+ }
+ var comp = ( typeof oComp == "object" ) ?
+ (typeof oComp.comp == "undefined" ) ?
+ diffCompare : oComp.comp : oComp;
+ if ( typeof oComp == "object" &&
+ typeof oComp.priorParse != "undefined" ) {
+ if ( typeof oComp.priorParse == "object" ) {
+ for ( var i=0; i < oComp.priorParse.length; i++) {
+ var retn = oComp.priorParse[i](UserAns);
+ if ( retn == null ) return null;
+ }
+ } else {
+ var retn = oComp.priorParse(UserAns);
+ if ( retn == null ) return null;
+ }
+ }
+% \begin{macrocode}
+ UserAns = ParseInput(UserAns);
+ CorrAns = ParseInput(CorrAns);
+ indepVar = TypeParameters(indepVar);
+ if (!ok2Continue) return null;
+ success=randomPointCompare(
+ n,domain,indepVar,epsilon,CorrAns,UserAns,comp);
+ if ( success == null ) { eqAppAlert(%
+\eqSyntaxErrorUndefVar,3); return null; }
+ return notifyField(success, flag, fieldname);
+}
+% \end{macrocode}
+% \texttt{ProcRespListComplex}, contributed by Bruce Wagner, extends
+% \texttt{ProcRespComplex} to lists; an ordered listing of complex responses.
+% Sample syntax:
+%\begin{verbatim}
+%If $z=4(\cos x+i\sin x)$, compute $z^2$ and $z^3$, in that order.
+%\RespBoxMath{16*cis(2x),64*cis(3x)}{4}{0.0001}{[0,1]}*{ProcRespListComplex}
+%\end{verbatim}
+% \begin{macrocode}
+function ProcRespListComplex(flag,CorrAns,n,epsilon,%
+domain,indepVar,oComp) {
+ ok2Continue = true;
+ if (!ProcessIt) return null;
+ var fieldname = event.target.name;
+ var UserAns = event.value;
+ UserAns = stripWhiteSpace(UserAns);
+ CorrAns = stripWhiteSpace(CorrAns); // sets ok2Continue
+ if ( !ok2Continue ) return null;
+ var isSpecResp=false;
+ for ( var i=0; i<aDlLibSpecResp.length; i++) {
+ if (CorrAns == aDlLibSpecResp[i]) {
+ isSpecResp=true; break;
+ }
+ }
+ for ( var i=0; i<aDlLibSpecResp.length; i++) {
+ if (UserAns == aDlLibSpecResp[i]) {
+ success = (CorrAns == UserAns);
+ return notifyField(success, flag, fieldname);
+ }
+ }
+ if (isSpecResp) return notifyField(false, flag, fieldname);
+ UserAns = UserAns.replace(/,+/g, ",");
+ UserAns = UserAns.replace(/,$/, "");
+ UserAns = UserAns.replace(/^,/, "");
+ event.value = UserAns;
+ var aUserAns = UserAns.split(",");
+ var aCorrAns = CorrAns.split(",");
+ if ( aUserAns.length != aCorrAns.length )
+ return notifyField(false, flag, fieldname);
+ var numCorrect = 0;
+ var match = 0;
+ for ( var i=0; i< aCorrAns.length; i++) {
+ match = 0;
+ event.value = aUserAns[i];
+ var retn = ProcRespComplex(
+ flag,aCorrAns[i],n,epsilon,domain,indepVar,oComp);
+ event.value = UserAns;
+ if ( retn == null ) return null;
+ numCorrect += (retn) ? 1 : 0;
+ }
+ var success = (numCorrect == aCorrAns.length);
+ return notifyField(success, flag, fieldname);
+}
+% \end{macrocode}
+% \texttt{ProcRespSetComplex}, contributed by Bruce Wagner, extends
+% \texttt{ProcRespComplex} to sets; an un-ordered listing of complex responses.
+%\begin{verbatim}
+% Find all real and complex solutions of the equation $x^2=-9$. \\
+% Express your answer(s) in rectangular form $a+bi$.
+% \RespBoxMath{3i,-3i}{4}{0.0001}{[0,1]}*{ProcRespSetComplex}
+%\end{verbatim}
+% \begin{macrocode}
+function ProcRespSetComplex(flag,CorrAns,n,epsilon,%
+domain,indepVar,oComp) {
+ ok2Continue = true;
+ if (!ProcessIt) return null;
+ var fieldname = event.target.name;
+ var UserAns = event.value;
+ UserAns = stripWhiteSpace(UserAns);
+ CorrAns = stripWhiteSpace(CorrAns); // sets ok2Continue
+ if ( !ok2Continue ) return null;
+ var isSpecResp=false;
+ for ( var i=0; i<aDlLibSpecResp.length; i++) {
+ if (CorrAns == aDlLibSpecResp[i]) {
+ isSpecResp=true; break;
+ }
+ }
+ for ( var i=0; i<aDlLibSpecResp.length; i++) {
+ if (UserAns == aDlLibSpecResp[i]) {
+ success = (CorrAns == UserAns);
+ return notifyField(success, flag, fieldname);
+ }
+ }
+ if (isSpecResp) return notifyField(false, flag, fieldname);
+ UserAns = UserAns.replace(/,+/g, ",");
+ UserAns = UserAns.replace(/,$/, "");
+ UserAns = UserAns.replace(/^,/, "");
+ event.value = UserAns;
+ CorrAns = stripWhiteSpace(CorrAns);
+ if (!ok2Continue) return null;
+ var aUserAns = UserAns.split(",");
+ var aCorrAns = CorrAns.split(",");
+ if ( aUserAns.length != aCorrAns.length )
+ return notifyField(false, flag, fieldname);
+ var numCorrect = 0;
+ var match = 0;
+ for ( var i=0; i< aCorrAns.length; i++) {
+ match = 0;
+ for ( var j=i; j< aUserAns.length; j++) {
+ event.value = aUserAns[j];
+ var retn = ProcRespComplex(%
+flag,aCorrAns[i],n,epsilon,domain,indepVar,oComp);
+ event.value = UserAns;
+ if ( retn == null ) return null;
+ if (retn==1) {
+ var temp=aUserAns[j];
+ aUserAns[j]=aUserAns[i];
+ aUserAns[i]=temp;
+ match = match + 1;
+ }
+ }
+ numCorrect += (match) ? 1 : 0;
+ }
+ var success = (numCorrect == aCorrAns.length);
+ return notifyField(success, flag, fieldname);
+}
+function changeArgs4Cis(str) {
+ var re =/cis\(/g;
+ while ( (aP=re.exec(str) ) != null ) {
+ var LeftP=re.lastIndex;
+ var RightP=FindBalP(str,re.lastIndex,1);
+ str = str.substring(0,RightP)
+ +",i"+str.substring(RightP);
+ }
+ return str;
+}
+\end{newsegment}
+\end{library@holding}
+% \end{macrocode}
+% \subsubsection{\texttt{satisfyEq}}
+%
+% \IndexOpt{satisfyEq} These functions are used for questions where the student is asked to enter one or more
+% points that satisfy a given equation $ F = G $. For these types of problems, there
+% are infinity many correct answers.
+%
+% \begin{macrocode}
+\@ifcheckedout{satisfyEq}
+\newcommand{\notifyWrongNumEntries}{\def\satisfyEqNotify{true}}
+\def\satisfyEqNotify{false}
+\newcommand{\wrongNumEntriesMsg}{"You don't have the correct number
+ of entries in your coordinate points. Expecting "+_n
+ +" entries per point." }
+\newcommand{\eqSyntaxErrorNoParens}{"Syntax Error: Enter the point
+ using parentheses, for example (1,2) or (1,2,3), as applicable."}
+\newcommand{\eqNonzeroEntries}{"Syntax Error: All entries are required
+ to be nonzero, try again."}
+\newcommand{\eqTooManyEntries}{"You've entered more points than
+ requested, enter only "+l+" points."}
+\newcommand{\eqTooFewEntries}{"You've entered fewer points than
+ requested, enter only "+l+" points."}
+\newcommand{\eqDuplEntries}{"One or more points are the same,
+ provide "+l+" distinct points."}
+\fi
+\begin{library@holding}{satisfyEq}
+\begin{newsegment}
+ {dljslib: Support for n-tuple input to Satisfy an Equation}
+% \end{macrocode}
+%\DescribeMacro{ProcRespEvalEq} is not based on random point generation, as
+%all the other \textsf{exerquiz} functions are. Here, the user enters
+%numerical data, the function then verifies the data entered satisfies the
+%given equation. The role of \texttt{CorrAns} has changed. If the equation
+%is $ F = G $, then $ F - G $ should be passed as the
+%\texttt{CorrAns} variable. This function obeys the value of
+%\texttt{epsilon} but ignores the number of iterations (\verb!{1}! below),
+%and the domain of the variables (\cs{ixdna} below) \cs{ixdna} is a special
+%command that can be used for this parameter, in this context only. The \texttt{oComp}
+%parameter is also ignored.
+%\medskip\noindent\textbf{Sample Use.}
+%\begin{verbatim}
+%\item Enter a point that lies on the line $2x+3y=6$,\\[3pt]
+% $\text{A point is }\RespBoxMath[\rectW{.75in}
+% \textSize{0}]{2x+3y-6}(xy){1}{.0001}{[0,1]x[0,1]}*{ProcRespEvalEq}
+% \CorrAnsButton{various, such as (0,2)}$
+%\end{verbatim}
+% The demo document is \texttt{satisfy\_eq.tex}.
+%\changes{v1.9}{2011/06/24}{Added \texttt{ProcRespEvalEq} in response to
+% a problem posed by David Arnold.}
+% \begin{macrocode}
+var bNotifyWrngNumEntries=\satisfyEqNotify;
+function ProcRespEvalEq(flag,CorrAns,n,epsilon,domain,indepVar,oComp){
+ var retn=_ProcRespEvalEq(true,flag,CorrAns,n,epsilon,indepVar);
+ return retn;
+}
+% \end{macrocode}
+% \DescribeMacro{ProcRespEvalEqNonZero} is the same as \texttt{ProcRespEvalEq}, but requires
+% all entries to be nonzero.
+% \begin{macrocode}
+function ProcRespEvalEqNonZero(flag,CorrAns,n,epsilon,%
+domain,indepVar,oComp) {
+ var retn=_ProcRespEvalEq(false,flag,CorrAns,n,epsilon,indepVar);
+ return retn;
+}
+% \end{macrocode}
+% \begin{macrocode}
+function _ProcRespEvalEq(allowzero,flag,CorrAns,n,epsilon,indepVar)
+{
+ ok2Continue = true;
+ if (!ProcessIt) return null;
+ var fieldname = event.target.name;
+ var UserAns = event.value;
+ UserAns = stripWhiteSpace(UserAns);
+ if (!ok2Continue) return null;
+ if ( (UserAns.charAt(0) != "\(") || %
+(UserAns.charAt(UserAns.length-1) != "\)"))
+ return eqAppAlert(\eqSyntaxErrorNoParens,3), null;
+ UserAns=UserAns.substring(1,UserAns.length-1);
+ var aUserAns = UserAns.split(",");
+% \end{macrocode}
+% Make sure all of UserAns are numbers
+% \begin{macrocode}
+ for ( var i=0; i < aUserAns.length; i++) {
+ try {
+ if (isNaN(eval(aUserAns[i]))) return syntaxError(), null;
+ if (!allowzero && (eval(aUserAns[i])==0))
+ return eqAppAlert(\eqNonzeroEntries,3), null;
+ } catch(e) { return syntaxError(), null; }
+ }
+ var _v = TypeParameters(indepVar);
+ var _V = _v.split(","); // e.g. _V[0] = "i:x"
+ var _n = _V.length;
+ if ( aUserAns.length != _n) {
+ if (bNotifyWrngNumEntries)
+ return eqAppAlert(\wrongNumEntriesMsg,3), null;
+ else
+ return notifyField(false, flag, fieldname);
+ }
+% \end{macrocode}
+% The following code is taken from \texttt{diffCompare}, it uses a "safe" technique
+% for evaluating an expression.
+% \begin{macrocode}
+ for (var _i=0; _i < _n; _i++) {
+ if (_V[_i].charAt(0) == "r" )
+ eval("var "+_V[_i].charAt(2)+"="+aUserAns[_i]+";");
+ else // assume type "i"
+ eval("var "+_V[_i].charAt(2)+"="+Math.ceil(aUserAns[_i])+";");
+ }
+ var UserInput=ParseInput(CorrAns);
+ var UserAns=eval(UserInput);
+ success=(Math.abs(UserAns) < epsilon)?true:false;
+ return notifyField(success, flag, fieldname);
+}
+% \end{macrocode}
+% This function takes a semi-colon delimited list of ordered n-tuples.
+% The \texttt{CorrAns} parameter is of the form \texttt{n\_pairs; F-G}
+% \begin{macrocode}
+function ProcRespEvalEqList(flag,CorrAns,n,epsilon,domain,%
+indepVar,oComp) {
+ var retn=_ProcRespEvalEqList(true,flag,CorrAns,n,epsilon,indepVar);
+ return retn;
+}
+function ProcRespEvalEqListNonZero(flag,CorrAns,n,epsilon,domain,%
+indepVar,oComp){
+ var retn=_ProcRespEvalEqList(false,flag,CorrAns,n,epsilon,indepVar);
+ return retn;
+}
+function _ProcRespEvalEqList(allowzero,flag,CorrAns,n,epsilon,indepVar)
+{
+ ok2Continue = true;
+ if (!ProcessIt) return null;
+ var fieldname = event.target.name;
+ var UserAns = event.value;
+ UserAns = stripWhiteSpace(UserAns);
+ if (!ok2Continue) return null;
+ var success;
+% \end{macrocode}
+% Remove any semicolons at end of line, then remove any duplicated semicolons.
+% This is done to prevent simple errors by user input.
+% \begin{macrocode}
+ UserAns=UserAns.replace(/;+$/,"");
+ UserAns=UserAns.replace(/;+/g,";");
+ var aUsersArray = new Array();
+% \end{macrocode}
+% Split the user's answer up by the delimiting semi-colon.
+% \begin{macrocode}
+ var aUserAns = UserAns.split(";");
+% \end{macrocode}
+% First component is the number of points expected, second component is \texttt{F(x)-G(x)}
+% \begin{macrocode}
+ var aCorrAns = CorrAns.split(";");
+ var l = aCorrAns[0];
+% \end{macrocode}
+% If the user gave too many answers, broadcast alert.
+% \begin{macrocode}
+ if (l < aUserAns.length )
+ return eqAppAlert(\eqTooManyEntries,3), null;
+% \end{macrocode}
+% If the user gave too few answers, broadcast alert.
+% \begin{macrocode}
+ if (l > aUserAns.length )
+ return eqAppAlert(\eqTooFewEntries,3), null;
+ var _v = TypeParameters(indepVar);
+ var _V = _v.split(","); // e.g. _V[0] = "i:x"
+% \end{macrocode}
+% \texttt{\_n} is the number of variables
+% \begin{macrocode}
+ var _n = _V.length;
+% \end{macrocode}
+% \texttt{testFunc} is a random linear function of the form
+% \texttt{ax+by+...}, where \texttt{a} and \texttt{b} are selected at random.
+% \begin{macrocode}
+ var testFunc="";
+ for (var _i=0; _i < _n; _i++)
+ testFunc += ("+"+(Math.random()*9)+"*"+_V[_i].charAt(2));
+% \end{macrocode}
+%\changes{v1.9d}{2012/05/13}{Introduced \texttt{iCorrect} variable}
+% Introduced \texttt{iCorrect} variable to correct check each of the
+% user's pairs of responses.
+% \begin{macrocode}
+ var isCorrect=1;
+ for (var pair=0; pair< l; pair++) {
+% \end{macrocode}
+% We require each ordered pair to be enclosed in parentheses
+% \begin{macrocode}
+ if ( (aUserAns[pair].charAt(0) != "\(") || %
+(aUserAns[pair].charAt(aUserAns[pair].length-1) != "\)"))
+ return eqAppAlert(\eqSyntaxErrorNoParens,3), null;
+% \end{macrocode}
+% Strip away the parentheses, so for example, \texttt{UserAnsPair="3,5"}
+% \begin{macrocode}
+ UserAnsPair=aUserAns[pair].substring(1,aUserAns[pair].length-1);
+% \end{macrocode}
+% \texttt{aUserAnsPair} an the array that contains the components of
+% \texttt{aUserAns[pair]}
+% \begin{macrocode}
+ var aUserAnsPair = UserAnsPair.split(",");
+% \end{macrocode}
+% See if each component is a number
+% \begin{macrocode}
+ for ( var i=0; i < aUserAnsPair.length; i++) {
+ try { if (isNaN(eval(aUserAnsPair[i]))) %
+return syntaxError(), null;
+% \end{macrocode}
+%\changes{v1.9d}{2012/05/13}{Corrected reference, changed
+%\texttt{aUserAns} to \texttt{aUserAnsPair}}
+% \texttt{(2012/05/13)} Corrected reference, changed \texttt{aUserAns} to \texttt{aUserAnsPair}
+% \begin{macrocode}
+ if (!allowzero && (eval(aUserAnsPair[i])==0))
+ return eqAppAlert(\eqNonzeroEntries,3), null;
+ } catch(e) { return syntaxError(), null; }
+ }
+% \end{macrocode}
+% If the number of components does not match the number of variables, this is an
+% error. Give the user a chance to correct it.
+% \begin{macrocode}
+ if ( aUserAnsPair.length != _n) {
+ if (bNotifyWrngNumEntries)
+ return eqAppAlert(\wrongNumEntriesMsg,3), null;
+ else
+ return notifyField(false, flag, fieldname);
+ }
+% \end{macrocode}
+% Evaluate each component of the user's answer.
+% \begin{macrocode}
+ for (var _i=0; _i < _n; _i++) {
+ if (_V[_i].charAt(0) == "r" )
+ eval ("var "+_V[_i].charAt(2)+"="+aUserAnsPair[_i]+";");
+ else // assume type "i"
+ eval ("var "+_V[_i].charAt(2)+"="%
++Math.ceil(aUserAnsPair[_i])+";");
+ }
+% \end{macrocode}
+% \texttt{UserInput} is misnamed, this is the answer the author provides,
+% for example, \texttt{2x+3y-6}. We parse it, then evaluate it (using the values
+% of the variables computed above.
+% \begin{macrocode}
+ var UserInput=ParseInput(aCorrAns[1]);
+ var UserAns=eval(UserInput);
+% \end{macrocode}
+% We also evaluate the test function \texttt{testFunc} for the values of the
+% variables computed above. We store the result in \texttt{aUsersArray}.
+% \begin{macrocode}
+ aUsersArray[pair]=eval(testFunc);
+% \end{macrocode}
+% If less than \texttt{epsilon}, we set \texttt{success} to \texttt{true},
+% or we return \texttt{false}
+% \begin{macrocode}
+ success=(Math.abs(UserAns) < epsilon)?true:false;
+% \end{macrocode}
+% If \texttt{success} is true, we multiply by 1, else we multiply by 0
+% \begin{macrocode}
+ isCorrect *=Number(success);
+ }
+% \end{macrocode}
+% If \texttt{isCorrect} is \texttt{1}, all comparisons were successful.
+% \begin{macrocode}
+ success=(isCorrect==1);
+% \end{macrocode}
+% Sort \texttt{aUsersArray} from least to greatest
+% \begin{macrocode}
+ var aOrderArray = aUsersArray.sort(function(a,b){return a-b});
+ var m = aUsersArray.length - 1;
+% \end{macrocode}
+% See if any two consecutive entries differ by a little, if not, we'll
+% say the two answers are the same and ask the user for distinct entries.
+% \begin{macrocode}
+ for (i=0; i<m; i++)
+ if (Math.abs(aUsersArray[i]-aUsersArray[i+1])<.0001)
+ return eqAppAlert(\eqDuplEntries,3), null;
+% \end{macrocode}
+% If we have not exited earlier, we exit now with \texttt{success} as \texttt{true} or \texttt{false}.
+% \begin{macrocode}
+ return notifyField(success, flag, fieldname);
+}
+\end{newsegment}
+\end{library@holding}
+% \end{macrocode}
+% \subsubsection{\texttt{useGermanNums}}
+% \IndexOpt{useGermanNums}A simple \texttt{ProcResp()} for processing numbers entered in a German format: 1234,56.
+% The Germans have a block delimiter of period or space (1.234,56 or 1 234,56).
+% The period is not supported and causes an alert box.
+%\changes{v1.9g}{2014/01/14}{Added a proc to accept the comma for the decimal mark}
+% \begin{macrocode}
+\@ifcheckedout{useGermanNums}
+\newcommand{\noDecPtGerMsg}{"A period (.) is not allowed in answer,
+ use German notation for numbers"}
+\fi
+\begin{library@holding}{useGermanNums}
+\begin{newsegment}
+ {dljslib: Support for process numbers in the German format}
+% \end{macrocode}
+% \DescribeMacro{ProcRespNumsDe} This segment only has one ProcResp function in it,
+% this is named \texttt{ProcRespNumsDe}.
+% \begin{macrocode}
+function ProcRespNumsDe (flag,CorrAns,n,epsilon,%
+domain,indepVar,oComp) {
+ if (!ProcessIt) return null;
+ ok2Continue = true;
+ var fieldname = event.target.name;
+ var UserAns = event.value;
+% \end{macrocode}
+% This is the only change over the \texttt{ProcResp}, we call
+% \texttt{\_ProcRespNumsDe}.
+% \begin{macrocode}
+ var success = _ProcRespNumsDe(flag,CorrAns,UserAns,n,%
+epsilon,domain,indepVar,oComp);
+ if ( success == -1 || !ok2Continue ) return null;
+ if ( success == null ) { return syntaxError(), null; }
+ return notifyField(success, flag, fieldname);
+}
+function _ProcRespNumsDe(flag,CorrAns,UserAns,n,epsilon,%
+domain,indepVar,oComp) {
+ ok2Continue = true;
+ CorrAns = ParseInput(CorrAns);
+ if (!ok2Continue) {
+ eqAppAlert("Syntax error in author's answer! Check console.", 3);
+ return null;
+ }
+% \end{macrocode}
+% Save \texttt{UserAns} as \texttt{UserAnsSave} and pass \texttt{UserAnsSave} to
+% \texttt{processSpecialParse} instead of \texttt{UserAns}.
+% \begin{macrocode}
+ var UserAnsSave=UserAns;
+ var reDe=/,/g;
+ var reDec=/\./g;
+% \end{macrocode}
+% Deny use of the traditional decimal point (.)
+% \begin{macrocode}
+ if ( reDec.test(UserAns) ) {
+ eqAppAlert(\noDecPtGerMsg,3);
+ return -1;
+ }
+% \end{macrocode}
+% Replace all occurrences of \texttt{","} with \texttt{"."}, for internal use
+% \begin{macrocode}
+ UserAns=UserAns.replace(reDe,".");
+ var comp = ( typeof oComp == "object" ) ?
+ (typeof oComp.comp == "undefined" ) ?
+ diffCompare : oComp.comp : oComp;
+ if ( (typeof(oComp)=="object") %
+&& (typeof(oComp.priorParse)!="undefined") ) {
+% \end{macrocode}
+% We pass \texttt{UserAnsSave} in case author uses \texttt{priorParse} etc.
+% \begin{macrocode}
+ var retn=processSpecialParse(oComp.priorParse,UserAnsSave);
+ if (retn==null) return -1;
+ }
+ var reComma=/,/;
+ if ( reComma.test(UserAns) ) {
+ eqAppAlert(\eqSyntaxErrorComma,3);
+ return -1;
+ }
+ UserAns = ParseInput(UserAns);
+ indepVar = TypeParameters(indepVar);
+ if (!ok2Continue) return null;
+ var success=randomPointCompare(n,domain,indepVar,%
+epsilon,CorrAns,UserAns,comp);
+ if ( success && (typeof(oComp)=="object") %
+&& (typeof(oComp.postParse)!="undefined") )
+ success=processSpecialParse(oComp.postParse,UserAns);
+ return success;
+}
+\end{newsegment}
+\end{library@holding}
+% \end{macrocode}
+% The following functions, which are \texttt{ProcResp}-types, were written for an online
+% grading system being developed by Drs.\ Bruce Wagner and David Arnold, and Mr.
+% Jacob Miles-Prystowsky. The descriptions given below were provided by the authors.
+% \begin{macrocode}
+\begin{library@holding}{unordered}
+% \end{macrocode}
+% \subsubsection{\texttt{unordered}}
+% \IndexOpt{unordered}The \texttt{unordered} option is deprecated.
+% \begin{macrocode}
+\begin{newsegment}{dljslib: Contrib - Processing Unordered Responses}
+/*
+** The ProcRespSetFormula function is now listed under the
+** setSupport option, titled 'dljslib: Support for Sets'
+*/
+\end{newsegment}
+\end{library@holding}
+% \end{macrocode}
+% \begin{macrocode}
+\@ifcheckedout{factors}
+\newcommand{\facNoPropForm}{"Factorization is not in the proper form,
+ try placing the constant, if any, at the beginning of the
+ factorization"}
+\newcommand{\noNotEncloseMonos}{"Do not enclose constants or
+ monomials in parentheses"}
+\fi
+\begin{library@holding}{factors}
+% \end{macrocode}
+% \subsubsection{\texttt{factors}}
+% \IndexOpt{factors}\texttt{ProcRespFactors} is for grading polynomial factoring
+% questions. For example, if a polynomial factors as
+% \verb!-5x^2(x-4)(x+2)!, then the answer is only unique up to the order of
+% factors and a change of sign on an even number of factors. For
+% example, it could also be written as \verb!-5x^2(x+2)(x-4)! or
+% \verb!5x^2(-x+4)(x+2)! or \verb!-5(x-4)(x+2)x^2!, etc. This script
+% should grade all of these correctly. (However, it will not allow
+% something like \verb!-(x-4)(x+2)5x^2!. The leading coefficient, if
+% there is one, must be placed at the beginning, which conforms to
+% our usual conventions.)
+%\begin{flushleft}
+% Usage:
+%\begin{verbatim}
+%\def\factorsbox#1#2#3{%
+% \RespBoxMath{#1}(#2){4}{1.0E-15}{#3}*{ProcRespFactors}}
+%Some question requiring a factored response
+%\factorsbox{3x(x^2+1)(2x-1)^3}{x}{[0,1]}
+%\end{verbatim}
+%\end{flushleft}
+% (2014/08/08) Rewrote portions of Bruce's code,
+% introduce the \texttt{getFactorArray} function, giving better
+% parsing and error feedback.
+%\changes{v1.9j}{2014/08/08}{Rewrote portions of Bruce's code,
+% introduce the function \texttt{getFactorArray}, giving better
+% parsing and error feedback.}
+% \begin{macrocode}
+\begin{newsegment}{dljslib: Contrib - Processing Factors as Responses}
+function ProcRespFactors(flag,CorrAns,n,epsilon,domain,indepVar,oComp)
+{
+ ok2Continue = true;
+ if (!ProcessIt) return null;
+ var fieldname = event.target.name;
+ var UserAns = event.value;
+ UserAns = stripWhiteSpace(UserAns);
+ if (!ok2Continue) return null;
+ var retn = _ProcResp(flag,CorrAns,UserAns,n,epsilon,%
+domain,indepVar,oComp);
+ if ( retn == -1 ) return null;
+ if ( retn == null ) return syntaxError(), null;
+ if ( retn == 0 ) {
+ var initialsuccess = false;
+ return notifyField(initialsuccess, flag, fieldname);
+ }
+ var aUserAns=new Array();
+ var aCorrAns=new Array();
+ var aNegCorrAns=new Array();
+ aUserAns=getFactorArray(UserAns);
+ if (aUserAns==null) {
+ eqAppAlert(\facNoPropForm, 3);
+ return null;
+ }
+ if (aUserAns==-1) return null;
+ aCorrAns=getFactorArray(CorrAns);
+ if ( aCorrAns==null) {
+ app.beep(); console.show();
+ console.println("Author error in factorization, its not in %
+the proper form");
+ return null;
+ }
+ for ( var i=0; i< aCorrAns.length; i++) {
+ aNegCorrAns[i] = "-" + "(" + aCorrAns[i] + ")"}
+ var numCorrect = 0, match=0,signflag=0;
+ if ( aUserAns.length != aCorrAns.length )
+ return notifyField(false, flag, fieldname);
+ for ( var i=0; i< aCorrAns.length; i++) {
+ match = 0;
+ for ( var j=i; j< aUserAns.length; j++) {
+ var retn = _ProcResp(flag,aCorrAns[i],aUserAns[j],%
+n,epsilon,domain,indepVar,oComp);
+ if ( retn == -1 ) return null;
+ if ( retn == null ) return syntaxError(), null;
+ if (retn==1) {
+ var temp=aUserAns[j];
+ aUserAns[j]=aUserAns[i];
+ aUserAns[i]=temp;
+ match = match + 1;
+ }
+ else {
+ var retn = _ProcResp(flag,aNegCorrAns[i],aUserAns[j],%
+n,epsilon,domain,indepVar,oComp);
+ if ( retn == -1 ) return null;
+ if ( retn == null ) return syntaxError(), null;
+ if (retn==1) {
+ var temp=aUserAns[j];
+ aUserAns[j]=aUserAns[i];
+ aUserAns[i]=temp;
+ match = match + 1;
+ signflag = signflag + 1;
+ }
+ }
+ }
+ numCorrect += (match) ? 1 : 0;
+ }
+ var success = ((numCorrect==aCorrAns.length)&&(signflag\%2==0));
+ if ( success == null ) return syntaxError(), null;
+ return notifyField(success, flag, fieldname);
+}
+% \end{macrocode}
+% This function identifies the factors of a factored polynomial, does some
+% rearrangement, and returns an array of the factors.
+% \begin{macrocode}
+function getFactorArray(str) {
+ var aFactors=new Array();
+ var i,j,front,factor,back,bInitGrped=true;
+% \end{macrocode}
+% First, see of the first factor is grouped.
+% \begin{macrocode}
+ if (str.charAt(0) != "\(" ) {
+% \end{macrocode}
+% Not grouped, we'll scan forward for the next left paren and
+% push the whole thing onto aFactors.
+% \begin{macrocode}
+ bInitGrped=false;
+ i=str.indexOf("\(");
+ if ( i != -1 ) {
+ aFactors.push(str.substring(0,i));
+%console.println("Initial factor: "+ str.substring(0,i));
+% \end{macrocode}
+% Reset the string, removing what we've pushed onto aFactos
+% \begin{macrocode}
+ str=str.substring(i);
+%console.println("new str="+str);
+ }
+ }
+%var scan=0
+% \end{macrocode}
+% We now identify grouped factors, push them onto the aFactors array
+% and reset the string.
+% \begin{macrocode}
+ while ( (i=str.indexOf("\(")) != -1 ) {
+%console.println("scan "+(++scan));
+ j=FindBalP(str,i,true);
+ front=str.substring(0,i);
+ factor=str.substring(i,j+1);
+ back = str.substring(j+1);
+% \end{macrocode}
+% See if this factor has multiplicities, if so, combine it with the factor
+% and push onto aFactors
+% \begin{macrocode}
+ if ( back.charAt(0) == "\^" ) {
+ getExp=back.match(/\^\d+/);
+ theExp=back.substring(0,getExp[0].length);
+ factor+=(theExp);
+ aFactors.push(factor);
+%console.println("Adding factor: " + factor);
+ back=back.substring(getExp[0].length);
+ } else {
+ aFactors.push(factor);
+%console.println("Adding factor: " + factor);
+ }
+ str=front + back;
+%console.println("new str="+str);
+% \end{macrocode}
+% Before we finish with this factor, we'll check whether this is an
+% enclosed monomial eg \verb!(-2x^2)!, the sign may not be present, but
+% we need to test for it. We test for a sign, and beyond that, we search
+% \texttt{+/-}. If we find \texttt{+/-}, we emit an warning box and we
+% return a \texttt{-1}, same as null, but indicates an alert has been given.
+% \begin{macrocode}
+ if ( factor.charAt(1)=="-" || factor.charAt(1)=="+" )
+ factor=factor.substring(2);
+ if (!/[+-]/.test(factor)) {
+ eqAppAlert(\noNotEncloseMonos, 3);
+ return -1;
+ }
+ }
+%console.println("finished with scan step");
+% \end{macrocode}
+% Added the initial un-grouped factor and exhausted all grouped factors
+% what's left over is out of place
+% \begin{macrocode}
+ if (/[A-Za-z]/.test(str)) {
+% \end{macrocode}
+% want to pluck off the variable with optional power only, any constant
+% is misplaced
+% \begin{macrocode}
+ var aExp = str.match(/[A-Za-z](\^\d+)*/);
+ factor=aExp[0];
+ i=aExp.index;
+ front=str.substring(0,i);
+ back=str.substring(i+factor.length);
+ str=front+back;
+ if (!bInitGrped) {
+ if (aFactors[0]=="-" || aFactors[0]=="+")
+ aFactors[0]=aFactors[0]+factor;
+ else
+ aFactors[0]="\("+aFactors[0]+"\)"+"\("+factor+"\)";
+%console.println("Modified first factor: " + aFactors[0]);
+ } else {
+ aFactors.push(str);
+%console.println("adding factor: " +str);
+ str="";
+ }
+ }
+% \end{macrocode}
+% finally be sure this factor is not just a \texttt{"+"} or \texttt{"-"}
+% \begin{macrocode}
+ if (aFactors[0]=="-"||aFactors[0]=="+"||isFinite(aFactors[0])){
+ factor=aFactors.shift();
+ aFactors[0]=factor+aFactors[0];
+ }
+%console.println("aFactors: " + aFactors.toSource());
+%console.println("Final str="+str);
+% \end{macrocode}
+% If \texttt{str} is non-empty, that usually means there is a constant left over
+% that was misplaced in the middle or end of the factorization. Otherwise, we return
+% the aFactors array.
+% \begin{macrocode}
+ return (str!="") ? null : aFactors
+}
+\end{newsegment}
+\end{library@holding}
+% \end{macrocode}
+% \begin{macrocode}
+\@ifcheckedout{point}
+\newcommand{\pointErrorMsgi}{%
+ "I'm looking for a point. You need to use proper point notation."}
+\newcommand{\pointErrorMsgii}{"Parentheses are not balanced."}
+\newcommand{\pointErrorMsgiii}{"Incorrect number of components.
+ The answer requires "+ aCorrAns.length+" components."}
+\def\pointEmptyCompMsgiv(#1){
+ "You entered nothing for the component " +(#1+1)
+ +" of your answer. Please enter a component for the point."}
+\fi
+\begin{library@holding}{point}
+% \end{macrocode}
+% \subsubsection{\texttt{point}}
+% \IndexOpt{point}The JS function \texttt{ProcPoint} is an almost exact copy of your
+% \texttt{ProcVec}, but uses parentheses instead of angle brackets as
+% delimiters. This is for questions that have a point $(x,y)$ as the
+% answer.
+%\begin{flushleft}
+% Usage:
+%\begin{verbatim}
+%\def\formulapointbox#1#2#3{%
+% \RespBoxMath{#1}(#2){4}{1.0E-15}{#3}*{ProcPoint}}
+%Some question requiring a point (ordered pair) response
+%\formulapointbox{(e^t,te^t)}{t}{[0,1]}
+%\end{verbatim}
+%\end{flushleft}
+% \begin{macrocode}
+\begin{newsegment}{dljslib: Contrib - Processing a Point Response}
+function ProcPoint(flag,CorrAns,n,epsilon,domain,indepVar,oComp)
+{
+ if (!ProcessIt) return null;
+ ok2Continue = true;
+ var i, success, truthCnt=1;
+ var aScalar, scalar = 1;
+ var fieldname = event.target.name;
+ var UserAns = event.value;
+ UserAns = stripWhiteSpace (UserAns); // sets ok2Continue
+ CorrAns = stripWhiteSpace(CorrAns);
+ if ( !ok2Continue ) return null;
+ var isSpecResp=false;
+ for ( var i=0; i<aDlLibSpecResp.length; i++) {
+ if (CorrAns == aDlLibSpecResp[i]) {
+ isSpecResp=true; break;
+ }
+ }
+ for ( var i=0; i<aDlLibSpecResp.length; i++) {
+ if (UserAns == aDlLibSpecResp[i]) {
+ success = (CorrAns == UserAns);
+ return notifyField(success, flag, fieldname);
+ }
+ }
+ if (isSpecResp) return notifyField(false, flag, fieldname);
+ if (!/[()]/.test(UserAns)) {
+ return eqAppAlert(\pointErrorMsgi, 3), null;
+ }
+ if (!CkBalP(UserAns,"(",")")) {
+ return eqAppAlert(\pointErrorMsgii, 3), null;
+ }
+% \end{macrocode}
+% If \texttt{oComp} is an object, then see if it has a \texttt{comp} property
+% \begin{macrocode}
+ var comp = ( typeof oComp == "object" ) ?
+ (typeof oComp.comp == "undefined" ) ?
+ diffCompare : oComp.comp : oComp;
+ CorrAns = CorrAns.replace(/[()]/g, ""); // strip off ( and )
+ UserAns = UserAns.replace(/[()]/g, "");
+% \end{macrocode}
+% The \texttt{comp} parameter, which has been changed to \texttt{oComp} can now be
+% an object. One property of this object is \texttt{comp}, handled above. Another
+% property is \texttt{priorParse}, this is a function that returns \texttt{null}
+% or \texttt{true}. This \texttt{priorParse} function allows for additional
+% filtering of the \texttt{UserAns} before parsing. If it returns \texttt{true},
+% we are ok to continue, if \texttt{null}, we don't like something the user has entered,
+% and ask him/her to change it.
+% \begin{macrocode}
+ if ( typeof oComp == "object" &&
+ typeof oComp.priorParse != "undefined" ) {
+% \end{macrocode}
+% Let's go ahead and allow \texttt{oComp.priorParse} be an array of functions.
+% \begin{macrocode}
+ if ( typeof oComp.priorParse == "object" ) {
+ for ( var i=0; i < oComp.priorParse.length; i++) {
+ var retn = oComp.priorParse[i](UserAns);
+ if ( retn == null ) return null;
+ }
+ } else {
+ var retn = oComp.priorParse(UserAns);
+ if ( retn == null ) return null;
+ }
+ }
+% Not convert each to an array
+ aUserAns = UserAns.split(",");
+ aCorrAns = CorrAns.split(",");
+ if (scalar != 1)
+ for (i=0; i<aUserAns.length; i++)
+ aUserAns[i]=""+scalar+"*"+aUserAns[i];
+ if (aCorrAns.length != aUserAns.length) {
+ eqAppAlert(\pointErrorMsgiii,3);
+ return null;
+ }
+ indepVar = TypeParameters(indepVar);
+ for (i=0; i<aCorrAns.length; i++) {
+ aCorrAns[i] = ParseInput(aCorrAns[i]);
+ if (!ok2Continue) {
+ eqAppAlert("Author Syntax error!", 3);
+ return null;
+ }
+ aUserAns[i] = ParseInput(aUserAns[i]);
+ if (aUserAns[i]==null) {
+ eqAppAlert(\pointEmptyCompMsgiv(i), 3);
+ return null;
+ }
+ if (!ok2Continue) return null;
+ success=randomPointCompare (
+ n,domain,indepVar,epsilon,aCorrAns[i],aUserAns[i],comp)
+ if ( success == null ) {
+ eqAppAlert(\eqSyntaxErrorUndefVar,3); return null; }
+ truthCnt *= (success) ? 1 : 0;
+%- if (success) truthCnt +=1;
+ }
+% return notifyField((truthCnt == 3), flag, fieldname);
+ return notifyField(!!truthCnt, flag, fieldname);
+}
+\end{newsegment}
+\end{library@holding}
+% \end{macrocode}
+% \begin{macrocode}
+\begin{library@holding}{intervals}
+% \end{macrocode}
+% \subsubsection{\texttt{intervals}}
+% \IndexOpt{intervals}\texttt{ProcRespIntervals} is for grading questions that have an
+% interval or union of intervals as the answer. Example answers
+% include:
+%\begin{verbatim}
+%(2,3)
+%[2,3]
+%(-inf,4)
+%(-inf,4]
+%(6,inf)
+%[6,inf)
+%(2,3)U[8,9)
+%(-inf,6)U(9,inf)
+%(-inf,6]U[7,8)U(9,inf)
+%\end{verbatim}
+% Here I've used "inf" for infinity and the capital letter "U" for
+% the union symbol. Note also that intervals in a union can appear
+% in any order.
+%\begin{flushleft}
+%The \texttt{ProcRespIntervals} function has been modified by Robert Marik
+%to allow intervals with end points \texttt{ln(2)}, \texttt{exp(4)} or \texttt{sqrt(3)}.
+%Thus, intervals of the form
+%\verb!(ln(2),ln(3)]U(exp(10),inf)! are recognized by this procedure.
+%\medskip\noindent
+% Usage:
+%\begin{verbatim}
+%\def\intervalbox#1{\RespBoxMath{#1}(infU){10}
+% {1.0E-4}{[0,1]x[0,1]x[0,1]x[0,1]}*{ProcRespIntervals}}
+%Some question requiring an interval or union of intervals
+%\intervalbox{(-inf,4]U[5,inf)}
+%\end{verbatim}
+% To avoid problems with unbalanced parentheses, as in this example,
+%\verb!\intervalbox{(-inf,4)U[5,inf)}!.
+% Here there is only one right parenthesis and two left parentheses. As a
+% workaround, escape the odd
+% parenthesis, like so
+%\verb!\intervalbox{(-inf,4)U[5,inf\eqbs)}!,
+% where \cs{eqbs} is defined in the \textsf{insdljs} package.
+%\end{flushleft}
+% \begin{macrocode}
+\begin{newsegment}{dljslib: Contrib - Processing Interval Responses}
+function ProcRespIntervals(flag,CorrAns,n,epsilon,domain,indepVar,oComp)
+{
+ ok2Continue = true;
+ if (!ProcessIt) return null;
+ var fieldname = event.target.name;
+ var UserAns = event.value;
+ UserAns = stripWhiteSpace(UserAns);
+ CorrAns = stripWhiteSpace(CorrAns);
+ if (!ok2Continue) return null;
+ UserAns = UserAns.replace(/inf/g, "x");
+ CorrAns = CorrAns.replace(/inf/g, "x");
+ indepVar = "x";
+ domain="[0,1]";
+ var aUserAns = UserAns.split("U");
+ var aCorrAns = CorrAns.split("U");
+ var numCorrect = 0;
+ var match = 0;
+ var matchparts = 0;
+ if ( aUserAns.length != aCorrAns.length )
+ return notifyField(false,flag, fieldname);
+ for ( var i=0; i< aCorrAns.length; i++) {
+ match = 0;
+ for ( var j=i; j< aUserAns.length; j++) {
+ CorrInt=aCorrAns[i];
+ UserInt=aUserAns[j];
+ var levaUser = UserInt.charAt(0);
+ var pravaUser = UserInt.charAt(UserInt.length-1);
+ var stredUser = UserInt.substring(1,UserInt.length-1);
+ UserInt = levaUser+","+stredUser+","+pravaUser;
+ var levaCorr = CorrInt.charAt(0);
+ var pravaCorr = CorrInt.charAt(CorrInt.length-1);
+ var stredCorr = CorrInt.substring(1,CorrInt.length-1);
+ CorrInt = levaCorr+","+stredCorr+","+pravaCorr;
+ var aCorrInt = CorrInt.split(",");
+ var aUserInt = UserInt.split(",");
+ if ( aUserInt.length != 4 )
+ return notifyField(false, flag, fieldname);
+ matchparts = 0;
+ if (aCorrInt[0] == aUserInt[0]) matchparts+=1;
+ if (aCorrInt[3] == aUserInt[3]) matchparts+=1;
+ var retn1 = _ProcResp(flag,aCorrInt[1],aUserInt[1],%
+n,epsilon,domain,indepVar,oComp);
+ if (retn1 == -1 ) return null;
+ if (retn1 == null) return syntaxError(), null;
+ if (retn1 == 1) matchparts+=1;
+ var retn2 = _ProcResp(flag,aCorrInt[2],aUserInt[2],%
+n,epsilon,domain,indepVar,oComp);
+ if (retn2 == -1 ) return null;
+ if (retn2 == null) return syntaxError(), null;
+ if (retn2 == 1) matchparts+=1;
+ if (matchparts == 4) {
+ var temp=aUserAns[j];
+ aUserAns[j]=aUserAns[i];
+ aUserAns[i]=temp;
+ match = match + 1;
+ }
+ }
+ numCorrect += (match) ? 1 : 0;
+ }
+ var success = (numCorrect == aCorrAns.length);
+ return notifyField(success, flag, fieldname);
+}
+\end{newsegment}
+\end{library@holding}
+% \end{macrocode}
+%
+% \subsection{Compare Functions}\label{s:compfunctions}
+%
+% \textbf{Used by Exerquiz.} A compare function is a JavaScript that compares the author's answer
+% with the user's answer. There may be a need for compare functions other than the default one provided
+% by \texttt{exerquiz}.
+%
+% \subsubsection{\texttt{indefIntegral}}\label{indefIntegral}
+% \IndexOpt{indefIntegral}The answer to an indefinite integral is non-unique; however all answers differ by a constant.
+% This compare function is used with indefinite integrals. See \texttt{jqzspec.tex} for an
+% example of the use of this function. Note the name of the function is \texttt{indefCompare}, this
+% is the name you use to call the function. An example of usage of this function can be found in
+% the sample file \texttt{jslib\_ex.tex} that comes with the Acro\TeX{} Bundle; from that file we
+% have:
+%\begin{verbatim}
+%$\displaystyle\int\sin(x)\,dx =
+% \RespBoxMath{-cos(x)}[intSin]{4}{.0001}{[0,1]}[indefCompare]$
+%\end{verbatim}
+% See also the file \texttt{jqzspec.tex} for more details.
+% \begin{macrocode}
+\begin{library@holding}{indefIntegral}
+\begin{newsegment}{dljslib: Indefinite Integral Handling}
+function indefCompare(_a,_c,_v,_F,_G) {
+ var eqC;
+ var aAB = _a.split(",");
+ var aXY = _c.split(",");
+ var _V = _v.split(","); // e.g. _V[0] = "i:x"
+ var _n = aXY.length
+ for (var _i=0; _i< _n; _i++) {
+ if (_V[_i].charAt(0) == "r" )
+ eval ( "var "+ _V[_i].charAt(2)
+ + " = " + aAB[2*_i] + ";");
+ else // assume type "i"
+ eval ( "var "+ _V[_i].charAt(2)
+ + " = " + Math.ceil(aAB[2*_i]) + ";");
+ }
+% var C = 0 is used to designate an arbitrary constant
+ var C = 0;
+ if ( app.viewerVersion >= 5)
+ {
+ var rtnCode = 0;
+ eval("try {if (isNaN(eqC = eval(_F)-eval(_G))) rtnCode=-1;}"
+ +" catch (e) { rtnCode=1; }");
+ switch(rtnCode)
+ {
+ case 0: break;
+ case 1: return null;
+ case -1: return -1;
+ }
+ }
+ else
+ if (isNaN(eqC = eval(_F)-eval(_G))) return -1;
+ for (var _i=0; _i< _n; _i++)
+ {
+ if (_V[_i].charAt(0) == "r" )
+ eval ( "var "+ _V[_i].charAt(2)
+ + " = " + aXY[_i] + ";");
+ else // assume type "i"
+ eval ( "var "+ _V[_i].charAt(2)
+ + " = " + Math.ceil(aXY[_i]) + ";");
+ }
+ _F = eval(_F);
+ if ( app.viewerVersion >= 5)
+ {
+ var rtnCode = 0;
+ eval("try { if(isNaN(_G = eval(_G))) rtnCode=-1; }"
+ +" catch (e) { rtnCode=1; }");
+ switch(rtnCode)
+ {
+ case 0: break;
+ case 1: return null;
+ case -1: return -1;
+ }
+ }
+ else
+ if(isNaN(_G = eval(_G))) return -1;
+ return Math.abs( _F - _G - eqC );
+}
+\end{newsegment}
+\end{library@holding}
+% \end{macrocode}
+%
+% \subsection{Filter User's Responses}
+% The following two function were contributed by Ross Moore and Frances
+% Griffin, and were taken from their
+% \href{http://rutherglen.ics.mq.edu.au/~macqtex/}{MacQ\TeX} online testing
+% system. See the sample file \texttt{integer\_tst.tex} for sample usage.
+% These two functions take \texttt{UserAns} as a parameter and return
+% \texttt{null} or \texttt{true} to signal the user expression is not an
+% acceptable response, or that it's ok for processing, respectively.
+% \begin{macrocode}
+\@ifcheckedout{nodec}
+\newcommand\nodecAlertMsg{%
+ "A decimal answer is not acceptable here.
+ Please express your answer using fractions, square roots,
+ e, log, etc."}
+\fi
+\begin{library@holding}{nodec}
+% \end{macrocode}
+% \subsubsection{\texttt{nodec}}
+% \IndexOpt{nodec}Do not allow the use of decimal numbers. (Just searches of ``.''.)
+% \begin{macrocode}
+\begin{newsegment}{dljslib: Contrib - No Decimals}
+function nodec(UserAns)
+{
+ var dot = /[\.\aebdecimalpoint]/;
+ if (dot.test(UserAns)) {
+ eqAppAlert(\nodecAlertMsg,3);
+ return null;
+ } else return true;
+}
+\end{newsegment}
+% \end{macrocode}
+% \begin{macrocode}
+\end{library@holding}
+\@ifcheckedout{noBinFac}
+\newcommand\noBinFactBinCoeffAlertMsg{%
+ "You may not use this notation here.
+ Please evaluate the binomial coefficient.
+ You may present your answer as a product rather
+ than calculating a very large number."}
+\newcommand\noBinFactPermAlertMsg{%
+ "You may not use this notation here.
+ Please evaluate the permutation.
+ You may present your answer as a product rather
+ than calculating a very large number."}
+\newcommand\noBinFactFactAlertMsg{%
+ "You may not use this notation here.
+ Please evaluate the factorial.
+ You may present your answer as a product rather
+ than calculating a very large number."}
+\fi
+\begin{library@holding}{noBinFac}
+% \end{macrocode}
+% \subsubsection{\texttt{noBinFac}}
+% \IndexOpt{noBinFac}Disallow binomial coefficients and factorials in math fill-ins.
+% \begin{macrocode}
+\begin{newsegment}{dljslib: Contrib - No Binomial Coefficients Allowed}
+aReFact = new Array(
+ /(?=\()?(\d+)(?=\))?!/,
+ /(?=\[)?(\d+)(?=\])?!/,
+ /(?=\{)?(\d+)(?=\})?!/
+);
+function noBinFac(UserAns)
+{
+ var bad = /(C\()/;
+ if (bad.test(UserAns)) {
+ eqAppAlert(\noBinFactBinCoeffAlertMsg,3);
+ return null;
+ }
+ bad = /(P\()/;
+ if (bad.test(UserAns)) {
+ eqAppAlert(\noBinFactPermAlertMsg,3);
+ return null;
+ }
+ for ( var i=0; i<aReFact.length; i++) {
+ if (aReFact[i].test(UserAns)) {
+ eqAppAlert(\noBinFactFactAlertMsg,3);
+ return null;
+ }
+ }
+ bad = /(fact)/;
+ if (bad.test(UserAns)) {
+ eqAppAlert(\noBinFactFactAlertMsg,3);
+ return null;
+ }
+ return true
+}
+\end{newsegment}
+\end{library@holding}
+% \end{macrocode}
+% \begin{macrocode}
+\@ifcheckedout{limitArith}
+\newcommand{\allowWrngNormSciNotn}{\def\allowWrngNSN{true}}
+\def\allowWrngNSN{false}
+\newcommand{\DecimalsOnlyErrorMsg}{%
+ "Enter only an integer, e.g., 17, or a decimal number, e.g. 12.4.
+ Using arithmetic operations or built in function is not acceptable
+ for this problem."}
+\newcommand{\NoProductsErrorMsg}{%
+ "Multiplication is not allowed for this problem."}
+\newcommand{\NoDivisionErrorMsg}{%
+ "Division is not allowed for this problem."}
+\newcommand{\NoAddOrSubErrorMsg}{%
+ "Neither addition nor subtraction is allowed for this problem."}
+\newcommand{\NoExpAllowedErrorMsg}{%
+ "The use of exponents is not allowed for this problem."}
+\newcommand{\NoTrigAllowedErrorMsg}{%
+ "The use of trig functions in this problem is not allowed."}
+\newcommand{\NoPiAllowedErrorMsg}{%
+ "The use of PI or pi is not allowed in this problem."}
+\newcommand{\NoTrigLogAllowedErrorMsg}{%
+ "The use of trig and log functions is not allowed
+ in this problem."}
+\newcommand{\sciNotSyntaxError}{"Enter the answer in
+ scientific notation."}
+\newcommand{\sciNotNormalForm}{"The scientific notation entered
+ is not in normalized form."}
+\newcommand{\NoNegExpMsg}{"No negative exponents permitted,
+ keep working!"}
+\fi
+\begin{library@holding}{limitArith}
+% \end{macrocode}
+% \subsubsection{\texttt{limitArith}}
+% \IndexOpt{limitArith}A collection of functions to limit the use of the arithmetic operations and to limit
+% the use of the trig functions. These functions are called through an optional parameter
+% of \cs{RespBoxMath}, as illustrated below and in the demo document \texttt{limarith.tex}.
+% \begin{macrocode}
+\begin{newsegment}{dljslib: Limit Arithmetic and Built in Functions}
+% \end{macrocode}
+% The \texttt{DecimalsOnly} is a function that takes only numbers, either integer or decimal.
+% Usage
+%\begin{verbatim}
+% $ 2.3 + 4.5=\RespBoxMath[\rectW{.75in}\textSize{0}]{6.8}{1}{.0001}{[0,1]}
+% [{priorParse: DecimalsOnly }]\CorrAnsButton{6.8}$
+%\end{verbatim}
+% \begin{macrocode}
+function DecimalsOnly(UserAns) {
+ UserAns = stripWhiteSpace(UserAns);
+ if ( !ok2Continue ) return null;
+ if( !isFinite( UserAns ) ) {
+ eqAppAlert(\DecimalsOnlyErrorMsg,3);
+ return null;
+ }
+ return true;
+}
+% \end{macrocode}
+% The function \texttt{NoProducts} requires that \texttt{ImplMulti} be taken.
+% An example of usage is
+%\begin{verbatim}
+%$ 3/4 = \RespBoxMath[\rectW{.75in}\textSize{0}]{.75}{1}{.0001}{[0,1]}
+% [{priorParse: new Array(NoDivision, NoProducts) }]\CorrAnsButton{.75}$
+%\end{verbatim}
+% In the above example, we first call \texttt{NoDivision} then \texttt{NoProducts}. This is
+% the way you can chain these filtering functions together.
+% \begin{macrocode}
+function NoProducts (UserAns) {
+ // Requires the ImplMulti option of dljslib
+ UserAns = stripWhiteSpace(UserAns);
+ if ( !ok2Continue ) return null;
+ UserAns = Ck4Products(UserAns);
+ if ( /\*/.test( UserAns ) )
+ return eqAppAlert(\NoProductsErrorMsg,3), null;
+ return true;
+}
+function NoDivision (UserAns) {
+ if ( /\//.test( UserAns) )
+ return eqAppAlert(\NoDivisionErrorMsg,3), null;
+ return true;
+}
+% \end{macrocode}
+% This function, as the name implies, denies the use of both addition and subtraction.
+% The algorithm I use here is that if a plus or minus follows anything but a left parenthesis,
+% we assume it is a binary operation of addition or subtraction, respectively.
+%\begin{verbatim}
+%$ 5.1 - 3.2 = \RespBoxMath[\rectW{.75in}\textSize{0}]{1.9}{1}{.0001}{[0,1]}
+% [{priorParse: NoAddOrSub }]\CorrAnsButton{1.9}$
+%\end{verbatim}
+% \begin{macrocode}
+function NoAddOrSub (UserAns) {
+ UserAns = stripWhiteSpace(UserAns);
+ if ( !ok2Continue ) return null;
+ UserAns=ChngAllGrpsToParens(UserAns);
+ var result;
+ var re = /.[+-]/g;
+ re.lastIndex = 0;
+ while ( (result = re.exec( UserAns )) != null ) {
+ if ( result[0].charAt(0) != "\(" )
+ return eqAppAlert(\NoAddOrSubErrorMsg,3), null;
+ }
+ return true;
+}
+% \end{macrocode}
+% Deny user the use of all arithmetic functions, including exponents.
+% \begin{macrocode}
+function NoArithAllowed (UserAns) {
+ var aNoArithmetic = new Array ( NoAddOrSub, NoProducts,
+ NoDivision, NoExpAllowed );
+ for ( var i = 0; i < aNoArithmetic.length; i++ )
+ if ( (retn = aNoArithmetic[i](UserAns)) == null ) return null;
+ return true;
+}
+% \end{macrocode}
+% Deny user the use of exponents.
+% \begin{macrocode}
+function NoExpAllowed (UserAns) {
+ // Requires the ImplMulti option of dljslib
+ UserAns = stripWhiteSpace(UserAns);
+ if ( !ok2Continue ) return null;
+ if ( /\^/.test( UserAns ) || /pow/.test( UserAns ) )
+ return eqAppAlert(\NoExpAllowedErrorMsg,3), null;
+ return true;
+}
+% \end{macrocode}
+% Deny user the use all trig functions, as well as the constants \texttt{PI} and \texttt{pi},
+% which are aliases for $\pi$. Example:
+%\begin{verbatim}
+%$ \sin(\pi/4) = \RespBoxMath[\rectW{.75in}\textSize{0}]
+% {sqrt(2)/2}{1}{.0001}{[0,1]}[{priorParse: NoTrigAllowed }]
+% \CorrAnsButton{sqrt(2)/2}$
+%\end{verbatim}
+%You can obviously build similar function to deny the use of logs or square roots, for example.
+%\changes{v1.5}{2007/02/05}
+%{
+% Removed \texttt{"pi"} and \texttt{"PI}" from the \texttt{aTrigFuncs array}, and added
+% \texttt{"arcsin"}, \texttt{"arccos"}, "\texttt{arctan"}.
+%}
+% \begin{macrocode}
+function NoTrigAllowed (UserAns)
+{
+ UserAns = stripWhiteSpace(UserAns);
+ if ( !ok2Continue ) return null;
+ var aTrigfuncs = new Array
+ ( "acos","asin","atan","cos", "sin", "tan","sec","csc","cot",
+ "arcsin", "arccos", "arctan"
+ );
+ var re, regexp;
+ re = /[a-zA-Z]{2,}/g;
+ aF = UserAns.match(re);
+ if ( aF != null ) {
+ for (var i=0; i < aF.length; i++)
+ {
+ for(var j=0; j < aTrigfuncs.length; j++) {
+ if ( aF[i].indexOf(aTrigfuncs[j]) != -1 )
+ return eqAppAlert(\NoTrigAllowedErrorMsg,3), null;
+ }
+ }
+ }
+ return true;
+}
+function NoPiAllowed(UserAns)
+{
+ UserAns = stripWhiteSpace(UserAns);
+ if ( !ok2Continue ) return null;
+ var re=/PI|pi/;
+ if ( re.test(UserAns) )
+ return eqAppAlert(\NoPiAllowedErrorMsg,3), null;
+ return true;
+}
+function NoTrigLogAllowed (UserAns)
+{
+ UserAns = stripWhiteSpace(UserAns);
+ if ( !ok2Continue ) return null;
+ var aTrigfuncs = new Array
+ ( "acos","asin","atan","cos", "sin",
+ "tan","sec","csc","cot", "arcsin", "arccos", "arctan",
+ "logc","log", "ln"
+ );
+ var re, regexp;
+ re = /[a-zA-Z]{2,}/g;
+ aF = UserAns.match(re);
+ if ( aF != null ) {
+ for (var i=0; i < aF.length; i++)
+ {
+ for(var j=0; j < aTrigfuncs.length; j++) {
+ if ( aF[i].indexOf(aTrigfuncs[j]) != -1 )
+ return eqAppAlert(\NoTrigLogAllowedErrorMsg,3), null;
+ }
+ }
+ }
+ return true;
+}
+% \end{macrocode}
+%\changes{v1.9a}{2011/07/15}{Added \texttt{sciNotResp} and \texttt{SciNotResp}}
+% This filter function checks to see if the user's input is in the form of (normalized)
+% scientific notation: \verb![+-]*d[.d*]*[ ]*E[+-]*d+!, written in pseudo-reg expr.
+% \begin{macrocode}
+var bAllowWrngNormSciNotn=false;
+var bItsNormSciNot=false;
+function SciNotNoNotify(UserAns) {
+ bAllowWrngNormSciNotn=true;
+ var rtn=SciNotResp(UserAns);
+ return rtn;
+}
+function SciNotResp(UserAns) {
+ bItsNormSciNot=false;
+ var _sciNotation, _a, _b;
+ _sciNotation = /^[+-]*(\d*)*(\.\d*)*E[+-]*\d+$/;
+ UserAns=stripWhiteSpace(UserAns);
+ if ( (_a=_sciNotation.exec(UserAns)) !=null) {
+ if (isNaN(_a[1])) {
+ if (bAllowWrngNormSciNotn)
+ return true;
+ else
+ return eqAppAlert(\sciNotNormalForm,3), null;
+ }
+ if (isNaN(_a[2])) _a[2]=0;
+ _b = Math.abs(Number(_a[1])+Number(_a[2]));
+ if ( _b < 1 || _b >= 10 ) {
+ if (bAllowWrngNormSciNotn)
+ return true;
+ else
+ return eqAppAlert(\sciNotNormalForm,3), null;
+ }
+ else {
+ bItsNormSciNot=true;
+ return true
+ }
+ } else
+ return eqAppAlert(\sciNotSyntaxError,3), null;
+}
+function sciNotResp(UserAns) {return SciNotResp(UserAns);}
+function postSciNotResp(UserAns) {
+ bAllowWrngNormSciNotn=false;
+ return bItsNormSciNot;
+}
+% \end{macrocode}
+%\changes{v1.9a}{2011/07/15}{Added \texttt{noNegExp}}
+% \texttt{noNegExp} does not allow negative exponents in the answer
+% \begin{macrocode}
+function NoNegExp (UserAns) {
+ var re=/(\^|\^\()+-/g;
+ UserAns=ChngAllGrpsToParens(UserAns);
+ if (re.test(UserAns))
+ return eqAppAlert(\NoNegExpMsg,3), null;
+ return true;
+}
+function noNegExp(UserAns){return NoNegExp(UserAns)}
+\end{newsegment}
+\end{library@holding}
+% \end{macrocode}
+% \subsection{Additional mathematical functions}
+% The following are contributions by Ross Moore and Frances Griffin, from their work
+% on \href{http://rutherglen.ics.mq.edu.au/~macqtex/}{MacQ\TeX}.
+% These define function of an integer variable: combinations, permutations and factorials.
+% These can be used for math fill-ins.
+% \begin{macrocode}
+\begin{library@holding}{combinatorics}
+% \end{macrocode}
+% \subsubsection{\texttt{combinatorics}}
+% Combinatorial function\IndexOpt{combinatorics}
+% \begin{macrocode}
+\begin{newsegment}{dljslib: Contrib - Combinatorial Functions}
+function ch(n,r)
+{
+ if ((n==r)||(r==0)) return(1);
+ if ((n==(r+1))||(r==1)) return(n);
+ if (r > (n-r))
+ var coeff = factorialCancel(
+ expandFactorial(r+1,n),expandFactorial(1,n-r));
+ else
+ var coeff = factorialCancel(
+ expandFactorial(n-r+1,n),expandFactorial(1,r));
+ return (eval(coeff));
+}
+function perm(n,r)
+{
+ if (r==0) return(1);
+ else
+ var coeff = factorialCancel(
+ expandFactorial(n-r+1,n),expandFactorial(1,n-r));
+ return (eval(coeff));
+}
+% \end{macrocode}
+% \texttt{factorialCancel} and \texttt{expandFactorial} are needed by \texttt{binomialCoeff}
+% \begin{macrocode}
+function expandFactorial(lo,hi)
+{
+ var f = lo;
+ for (var i=lo+1;i<=hi;i++) f = i+"*"+f;
+ return f;
+}
+% \end{macrocode}
+% \texttt{factorialCancel} cancels common factors in \texttt{num} and \texttt{denom} using strings produced
+% by \texttt{expandFactorial}. It expects tails of factorials to have already been cancelled.
+% \begin{macrocode}
+function factorialCancel(top,bot)
+{
+ var num = top.split("*");
+ var denom = bot.split("*");
+ var len = denom.length;
+ var temp = 0;
+ var i, j;
+ for (i=0;i<=len-1;i++) {
+ for (j=0;j<=len-1;j++) {
+ temp = num[i]/denom[j];
+ if ((temp - Math.round(temp)) == 0) {
+ num[i] = temp;
+ denom[j] = 1;
+ }
+ }
+ }
+ var t = denom.join("");
+ var reg = /[^1]/;
+ if (reg.test(t)) {
+ temp = factorialCancel(denom.join("*"),num.join("*"));
+ } else {
+ temp = num.join("*");
+ }
+ return (temp);
+}
+function fact(num)
+{
+ var tot = 1;
+ for (var r=1; r <= num; r++) tot *= r;
+ return(tot);
+}
+\end{newsegment}
+\end{library@holding}
+% \end{macrocode}
+% \subsection{\texttt{ParseInput} Extensions}\label{s:parseinput}
+% The default behavior for processing a math fill-in question is to
+% require the student to insert `\texttt*' for multiplication and to
+% enclose any function being raised to a power in parentheses, e.g.
+% \verb+x*(sin(x))^2+. This becomes quite tiresome if the expression to be entered
+% is complicated. The following two functions enable the student to use
+% `implied multiplication' and simplified exponentiation.
+%
+% \IndexOpt{ImplMulti}
+% The \texttt{ImplMulti} option loads two JavaScript functions, \texttt{Ck4Products()}
+% and \texttt{Ck4Exponents()}. The latter implements the notation, \verb+sin^2(x)+
+% for $\sin^2(x)$, equivalent to $(\sin(x))^2$. The exponent can be complex and enclosed
+% in parentheses, for example \verb-ln^(x+1)(x)-. The former function, \texttt{Ck4Products()},
+% inserts the multiplication symbol, `\texttt{*}', wherever implied. For example,
+% \verb+xysin(xy)+ becomes \verb+x*y*sin(x*y)+.
+% \begin{flushleft}
+% \textcolor{red}{Important:} If loaded, \texttt{Exerquiz} will automatically use these two functions.
+% \end{flushleft}
+% \begin{macrocode}
+\begin{library@holding}{ImplMulti}
+% \end{macrocode}
+% \subsubsection{\texttt{ImplMulti}}
+% \begin{macrocode}
+\begin{newsegment}{dljslib: Implied Multiplication}
+% \end{macrocode}
+% The JavaScript function \texttt{Ck4Products()} takes the user input and tries to
+% insert the multiplication symbol, `\texttt*', wherever implied.
+% \begin{macrocode}
+function Ck4Products(UserInput)
+{
+ var re, aR;
+% \end{macrocode}
+% Search through for matches with known functions
+% \begin{macrocode}
+ for (var i=0; i<JSf.length; i++)
+ {
+% \end{macrocode}
+% Here we mark the beginning of the left parenthesis for a
+% that follows one of the known functions. This way we can
+% distinguish between the built in JavaScript functions and
+% other sequences of letters that may represent products.
+% \begin{flushleft}
+% \textbf{Note:} In the regular
+% expression below, and elsewhere, when our search includes unbalanced parentheses
+% we need to triple escape them, i.e., instead of `\verb+\\(+' we write
+% `\verb+\\\(+'. See comments in \texttt{insdlis.dtx}.
+% \end{flushleft}
+% \begin{macrocode}
+ re = new RegExp("("+JSf[i]+")(\\\()","g");
+ UserInput = UserInput.replace(re, "\\$1@$2");
+% \end{macrocode}
+% If a known function has a letter or a right parenthesis to the left of it,
+% the user must want multiplication, make it so!
+% \begin{macrocode}
+ re = new RegExp("([\\w\\\)])(\\"+JSf[i]+")(@\\\()","g");
+ UserInput = UserInput.replace(re, "$1*$2$3");
+ }
+% \end{macrocode}
+% Now do the same type thing for known constants.
+% \begin{macrocode}
+ for (var i=0; i<JSc.length; i++)
+ {
+% \end{macrocode}
+% We mark the beginning and end of a known predefined constant
+% \changes{v1.6}{2007/02/23}
+%{
+% If we match up with "E", we do nothing. This is support of
+% using "E" for scientific notation.
+%}
+% \begin{macrocode}
+ re = new RegExp("("+JSc[i]+")","g");
+ if ( JSc[i] != "E" ) {
+ UserInput = UserInput.replace(re, "\\$1@");
+% \end{macrocode}
+% If a known constant has a letter or an right parenthesis to the left of it,
+% the user must want multiplication, make it so!
+% \begin{macrocode}
+ re = new RegExp("([\\w\\\)])(\\"+JSc[i]+")(@)","g");
+ }
+ }
+% \end{macrocode}
+% Search for a number or right paren followed by a letter or right paren
+% and insert *.
+%\changes{v1.6}{2007/02/23}
+%{
+% dps/change 02/23/07. In the search below, we exclude the search
+% for the letter "E", because it is used for scientific notation (called exponential)
+%}
+% notation in JavaScript).
+% \begin{macrocode}
+ UserInput = UserInput.replace(
+ /([\d\)])([A-DF-Za-z\(\\])/g, "$1*$2");
+% \end{macrocode}
+% Search for a right paren followed by a number.
+% \begin{macrocode}
+ UserInput = UserInput.replace(/(\))(\d)/g, "$1*$2");
+% \end{macrocode}
+% Search for a letter followed by a number of right paren, and insert *
+% \begin{macrocode}
+ UserInput = UserInput.replace(/([A-Za-z])([\d\(\\])/g, "$1*$2");
+% \end{macrocode}
+% If a predefined constant, such as \texttt{PI}, is multiplied on the
+% right by a factor, there will be a \texttt{@} followed by a letter or decimal.
+% \begin{macrocode}
+% re = /(@)([A-Za-z\d])/g;
+ UserInput = UserInput.replace(/(@)([A-Za-z\d])/g, "$1*$2");
+% \end{macrocode}
+% Now strip off the markers
+% \begin{macrocode}
+ UserInput = UserInput.replace(/(\\)([A-Za-z]{1,})(@)/g, "$2");
+% \end{macrocode}
+% Search through list of known functions for a match.
+% \begin{macrocode}
+% MacQTeX change dps/change 02/23/07
+ re = /[A-Za-z]{1,}/g;
+ while ( (aR = re.exec(UserInput)) != null)
+ {
+ for (var i=0; i<JSf.length; i++) if ( aR[0] == JSf[i]) break;
+ if ( i < JSf.length ) continue;
+ for (var i=0; i<JSc.length; i++) if ( aR[0] == JSc[i]) break;
+ if ( i < JSc.length ) continue;
+% \end{macrocode}
+% If there is no match with known functions or constants, then we need to assume
+% this word is a series of products, and separate them on their non-word
+% boundaries by a multiplication operator *. We will not be able to identify
+% mistyped functions, for example, if the user enters \texttt{cis(x)} instead of
+% \texttt{cos(x)}, this will end up as \texttt{c*i*s*(x)}. Ultimately,
+% the user will get a message that there is an error in the expression, but not
+% hints as to what the error is will not be given.
+% \begin{macrocode}
+ aR[0] = aR[0].replace(/([A-Za-z])\B/g,"$1*");
+ UserInput = UserInput.substring(0,aR.index)
+ +aR[0]+UserInput.substring(re.lastIndex);
+ }
+ return UserInput;
+}
+% \end{macrocode}
+% The function \texttt{Ck4Exponents()} attempts to convert the notation \verb+sin^2(x)+
+% into standard JavaScript format of \texttt{pow(sin(x),2)}.
+% \begin{macrocode}
+function Ck4Exponents(UserInput)
+{
+ var re, regexp, aP, RightP;
+ for (var i=0; (i<JSf.length) && (ok2Continue); i++)
+ {
+ re = new RegExp(JSf[i]+"\\^");
+ while ( re.test(UserInput) && (ok2Continue) )
+ {
+% \end{macrocode}
+% Here we seek to match an expression of the form: \verb+cos^(exp)(arg)+
+% \begin{macrocode}
+ regexp = new RegExp(JSf[i]+"\\^\\\(", "g");
+ if ( (aP = regexp.exec(UserInput)) != null ) {
+ // forward search
+ RightP=FindBalP(UserInput,regexp.lastIndex-1,1);
+ var offsetExp = RightP-regexp.lastIndex;
+ regexp = new RegExp(
+ JSf[i]+"\\^\\(.{"+offsetExp+"}\\)\\\(", "g");
+ regexp.lastIndex=0;
+ if ( (aP = regexp.exec(UserInput)) != null ) {
+ // forward search
+ RightP=FindBalP(UserInput,regexp.lastIndex-1,1);
+ var offsetArg = RightP - regexp.lastIndex;
+ regexp = new RegExp("("+JSf[i]
+ +")\\^\\((.{"+offsetExp+"})\\)\\((.{"
+ +offsetArg+"})\\)");
+ regexp.lastIndex=0;
+ if (regexp.test(UserInput))
+ UserInput=UserInput.replace(
+ regexp,"(pow($1($3),$2))");
+ else ok2Continue=false;
+ continue;
+ }
+ }
+% \end{macrocode}
+% Here we seek to match an expression of the form: \verb+cos^\pmNum.Num(arg)+
+% \begin{macrocode}
+ regexp = new RegExp(JSf[i]
+ +"\\^([a-zA-Z]|[+-]?\\d+\\.?\\d*|[+-]?\\d*\\.?\\d+)"
+ +"\\\(","g");
+ if ( (aP = regexp.exec(UserInput)) != null ) {
+ // forward search
+ RightP=FindBalP(UserInput,regexp.lastIndex-1,1);
+ regexp = new RegExp("("
+ +JSf[i]+")\\^([a-zA-Z]|[+-]?\\d+"
+ +"\\.?\\d*|[+-]?\\d*\\.?\\d+)\\((.{"
+ +eval(RightP-regexp.lastIndex)+"})\\)");
+ regexp.lastIndex=0;
+ if (regexp.test(UserInput))
+ UserInput=UserInput.replace(
+ regexp,"(pow($1($3),$2))");
+ else ok2Continue=false;
+ }
+ else ok2Continue=false;
+ }
+ }
+% \end{macrocode}
+% Now we do the same thing for declared constants.
+% \begin{macrocode}
+ for (var i=0; (i < JSc.length) && (ok2Continue); i++)
+ {
+ re = new RegExp(JSc[i]+"\\^", "g");
+ while ( re.test(UserInput) && (ok2Continue) )
+ {
+ re.lastIndex = 0;
+ aR = re.exec(UserInput);
+ if (UserInput.charAt(re.lastIndex)=='\(') {
+ // forward search
+ RightP=FindBalP(UserInput,re.lastIndex,1);
+ var offsetExp = RightP - re.lastIndex - 1;
+ re.lastIndex=0;
+ regexp = new RegExp("("+JSc[i]
+ +")\\^\\((.{"+offsetExp+"})\\)");
+ if ( regexp.test(UserInput) )
+ UserInput = UserInput.replace(
+ regexp, "(pow($1,$2))");
+ else ok2Continue = false;
+ }
+ else
+ {
+ re.lastIndex=0;
+ regexp = new RegExp("("+JSc[i]
+ +")\\^([a-zA-Z]|[+-]?\\d+\\.?\\d*|[+-]?\\d*\\.?\\d+)");
+ if (regexp.test(UserInput))
+ UserInput = UserInput.replace(
+ regexp, "(pow($1,$2))");
+ else ok2Continue = false;
+ }
+ }
+ }
+ return UserInput;
+}
+\end{newsegment}
+\end{library@holding}
+% \end{macrocode}
+% Finally, we have the ``back wrapper'' or ``back cover'' of the selected materials. We close the
+% \texttt{insDLJS} environment
+% \begin{macrocode}
+\begin{library@holding}{dljslib}
+\end{insDLJS*}
+\end{library@holding}
+% \end{macrocode}
+% \section{Out the Door and Close it behind you}
+% \dots and free up this write register.
+% \begin{macrocode}
+\immediate\closeout\dljslib@verbatim@out
+\advance\count17 by -1
+% \end{macrocode}
+% \section{Back Home with your Selections}
+% \begin{macrocode}
+\input{dljslib.ljs}
+%</package>
+% \end{macrocode}
+\endinput