% \iffalse %<*copyright> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% dljsLib.sty package, 2002-2-9 %% %% Copyright (C) 2001-2009 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. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % %\NeedsTeXFormat{LaTeX2e} %\ProvidesPackage{dljslib} % [2012/11/17 v1.9f Manage a Library of Document Level JavaScripts (dps)] %<*driver> \documentclass{ltxdoc} \usepackage[colorlinks,hyperindex]{hyperref} \pdfstringdefDisableCommands{\let\\\textbackslash}% \EnableCrossrefs \CodelineIndex \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} \PrintIndex \end{document} % % \fi % \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.} % %\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} \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} % 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} \DeclareAndRegister{noBinFac} \DeclareAndRegister{limitArith} \DeclareAndRegister{combinatorics} \DeclareAndRegister{setSupport} \DeclareAndRegister{complex} \DeclareAndRegister{satisfyEq} \DeclareAndRegister{unordered} \DeclareAndRegister{factors} \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 fillowing, % \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 finaly 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} % \begin{macro}{equations} % These routines process questions for which an equation is the expected answer. % \begin{macrocode} \newcommand\equationsAlertMsg{"An equation is expected"} \begin{library@holding}{equations} \begin{newsegment}{dljslib: Equation Handling} function ProcRespEq(flag,CorrAns,n,epsilon,a,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)) { app.alert(\equationsAlertMsg, 3); return null; } % \end{macrocode} % Check for commas, not permitted here. (2012/05/25) % \begin{macrocode} var reComma=/,/; if ( reComma.test(UserAns) ) { app.alert(\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) { app.alert("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 (a, indepVar, zCorrAns, zUserAns); if ( lambda == null ) { app.alert(\eqSyntaxErrorUndefVar,3); return null; }; if ( !ok2Continue ) return notifyField(false, flag, fieldname); zCorrAns = lambda + "*(" + zCorrAns + ")"; success=randomPointCompare (n,a,indepVar,epsilon, zCorrAns,zUserAns,comp) if ( success == null ) { app.alert(\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} % \end{macro} % \subsubsection{\texttt{vectors}}\label{vectors} % \begin{macro}{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} \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." } \begin{library@holding}{vectors} \begin{newsegment}{dljslib: Vector Handling} function ProcVec (flag,CorrAns,n,epsilon,a,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; % \end{macrocode} % Implement an ``undefined'' answer. Due to Ross-Griffin % \begin{macrocode} if (UserAns == "undefined") { success = (CorrAns == UserAns); return notifyField(success, flag, fieldname); } CorrAns = stripWhiteSpace (CorrAns); UserAns = stripWhiteSpace (UserAns); // sets ok2Continue if ( !ok2Continue ) return null; if (!/[<>]/.test(UserAns)) { app.alert(\vectorsErrorMsgi, 3); return null; } if (!CkBalP(UserAns,"<",">")) { app.alert(\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" ) { % \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; } } % \end{macrocode} % Not convert each to an array % \begin{macrocode} aUserAns = UserAns.split(","); aCorrAns = CorrAns.split(","); if (scalar != 1) for (i=0; i)} and % replace with \texttt{cis(,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) { app.alert(\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,a,indepVar,epsilon,CorrAns,UserAns,comp); if ( success == null ) { app.alert(% \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,a,indepVar,oComp) { ok2Continue = true; if (!ProcessIt) return null; var fieldname = event.target.name; var UserAns = event.value; UserAns = stripWhiteSpace(UserAns); UserAns = UserAns.replace(/,+/g, ","); UserAns = UserAns.replace(/,$/, ""); UserAns = UserAns.replace(/^,/, ""); event.value = UserAns; CorrAns = stripWhiteSpace(CorrAns); if (!ok2Continue) return null; % Implement a "none" answer. if (UserAns == "none") { success = (CorrAns == UserAns); return notifyField(success, flag, fieldname); } 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,a,indepVar,oComp); event.value = UserAns; if ( retn == null ) return null; numCorrect += (retn) ? 1 : 0; } var success = (numCorrect == aCorrAns.length); % var success = (numCorrect == aCorrAns.length) ? true : false; 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,a,indepVar,oComp) { ok2Continue = true; if (!ProcessIt) return null; var fieldname = event.target.name; var UserAns = event.value; UserAns = stripWhiteSpace(UserAns); UserAns = UserAns.replace(/,+/g, ","); UserAns = UserAns.replace(/,$/, ""); UserAns = UserAns.replace(/^,/, ""); event.value = UserAns; CorrAns = stripWhiteSpace(CorrAns); if (!ok2Continue) return null; % Implement a "none" answer. if (UserAns == "none") { success = (CorrAns == UserAns); return notifyField(success, flag, fieldname); } 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,a,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}} % % 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} \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."} \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} function ProcRespEvalEq(flag,CorrAns,n,epsilon,a,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,a,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 app.alert(\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 app.alert(\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) 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,a,indepVar,oComp){ var retn=_ProcRespEvalEqList(true,flag,CorrAns,n,epsilon,indepVar); return retn; } function ProcRespEvalEqListNonZero(flag,CorrAns,n,epsilon,a,% 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 app.alert(\eqTooManyEntries,3), null; % \end{macrocode} % If the user gave too few answers, broadcast alert. % \begin{macrocode} if (l > aUserAns.length ) return app.alert(\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 app.alert(\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 app.alert(\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) 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= 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} % \end{macro} % \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} \newcommand\nodecAlertMsg{% "A decimal answer is not acceptable here. Please express your answer using fractions, square roots, e, log, etc."% } \begin{library@holding}{nodec} % \end{macrocode} % \subsubsection{\texttt{nodec}} % Do not allow the use of decimal numbers. (Just searches of ``.''.) % \begin{macrocode} \begin{newsegment}{dljslib: Contrib - No Decimals} %var nodecAlertMsg=\nodecAlertMsg; function nodec(UserAns) { var dot = /[\.\aebdecimalpoint]/; if (dot.test(UserAns)) { app.alert(\nodecAlertMsg,3); return null; } else return true; } \end{newsegment} % \end{macrocode} % \begin{macrocode} \end{library@holding} \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."} \begin{library@holding}{noBinFac} % \end{macrocode} % \subsubsection{\texttt{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)) { app.alert(\noBinFactBinCoeffAlertMsg,3); return null; } bad = /(P\()/; if (bad.test(UserAns)) { app.alert(\noBinFactPermAlertMsg,3); return null; } for ( var i=0; i (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} % 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}} % \begin{macrocode} \begin{newsegment}{dljslib: Contrib - Processing Unordered Responses} % \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,a,indepVar,oComp) { ok2Continue = true; if (!ProcessIt) return null; var fieldname = event.target.name; var UserAns = event.value; UserAns = stripWhiteSpace(UserAns); UserAns = UserAns.replace(/,+/g, ","); UserAns = UserAns.replace(/,$/, ""); UserAns = UserAns.replace(/^,/, ""); event.value = UserAns; CorrAns = stripWhiteSpace(CorrAns); if (!ok2Continue) return null; % Implement a "none" answer. if (UserAns == "none") { success = (CorrAns == UserAns); return notifyField(success, flag, fieldname); } 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 % \end{macrocode} \endinput