% \iffalse %<*copyright> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% dljsLib.sty package, %% %% Copyright (C) 2001-2021 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} % [2021/04/04 v2.2 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 \makeatletter \let\@latex@warning@no@line\@gobble \makeatother \def\darg#1{\texttt{\char123\relax#1\char125\relax}} \def\CMD#1{\textbackslash#1} \let\pkg\textsf \let\opt\texttt \let\env\texttt \let\app\textsf \let\uif\textsf \def\STRUT{\rule{0pt}{14pt}} \def\negSTRUT{\rule[-8pt]{0pt}{0pt}} \def\nmpsep#1{\hskip-\marginparsep\texttt{#1}} \def\visispace{\symbol{32}} \def\ameta#1{\ensuremath{\langle\textit{\texttt{#1}}\rangle}} \def\meta#1{\textsl{\texttt{#1}}} \def\SUB#1{\ensuremath{{}_{\mbox{\scriptsize\ttfamily#1}}}} \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} % % \fi % % \InputIfFileExists{aebdonotindex.def}{\PackageInfo{dljslib}{Inputting aebdonotindex.def}} % {\PackageInfo{dljslib}{aebdonotindex.def cannot be found}} % % \changes{v2.1}{2017/08/11}{Corresponding to exerquiz v8.0, dljslib is modified to % conform to multi-letter variables.} % \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}\leavevmode % \IndexOpt{equations}^^A% % \IndexOpt{vectors}^^A% % \IndexOpt{indefIntegral}^^A% % \IndexOpt{ImplMulti}^^A% % \IndexOpt{nodec}^^A% % \IndexOpt{noBinFac}^^A% % \IndexOpt{limitArith}^^A% % \IndexOpt{combinatorics}^^A% % \IndexOpt{setSupport}^^A% % \noindent % 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} % \end{macrocode} % \leavevmode % Additional registered names. % \IndexOpt{unordered}^^A% % \IndexOpt{complex}^^A% % \IndexOpt{satisfyEq}^^A% % \IndexOpt{useGermanNums}^^A% % \IndexOpt{useDeNums}^^A% % \IndexOpt{useEnNums}^^A % \IndexOpt{factors}^^A% % \IndexOpt{point}^^A% % \IndexOpt{intervals}^^A% % \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} \DeclareOption{useDeNums}{\ExecuteOptions{useGermanNums}} \DeclareAndRegister{useEnNums} \DeclareAndRegister{factors} \DeclareAndRegister{point} \DeclareAndRegister{intervals} % \end{macrocode} % \begin{macrocode} \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{exerquiz}[2017/08/04] \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} \newcommand{\SyntaxErrorAuthor}{"Syntax error in author's answer! Check console."} \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,indepVars,oComp) { if (!ProcessIt) return null; ok2Continue = true; var success; var fieldname = event.target.name; % \end{macrocode} % Modified for multi-letter variables: remove one line, insert two. (2017/08/09) % \begin{macrocode} % var UserAns = event.value; var bSubstVars=(arguments.length>7); // dps17 var UserAns=(bSubstVars)?arguments[7]:event.value; 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(\SyntaxErrorAuthor,3); console.println("Syntax Error: " + CorrAns); return null; } zUserAns = ParseInput(zUserAns); if (!ok2Continue) return null; % convert vars to new format, if needed indepVars = TypeParameters(indepVars); var lambda = getNonZeroRatio(domain,indepVars,zCorrAns,zUserAns); if ( lambda == null ) { eqAppAlert(\eqSyntaxErrorUndefVar,3); return null; }; if ( !ok2Continue ) return notifyField(false,flag,fieldname); zCorrAns = lambda + "*(" + zCorrAns + ")"; success=randomPointCompare (n,domain,indepVars,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,indepVars,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]/.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)."} \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,indepVars,oComp) { ok2Continue = true; if (!ProcessIt) return null; var fieldname = event.target.name; % \end{macrocode} % Modified for multi-letter variables: remove one line, insert two. (2017/08/09) % \begin{macrocode} % var UserAns = event.value; var bSubstVars=(arguments.length>7); // dps17 var UserAns=(bSubstVars)?arguments[7]:event.value; UserAns = stripWhiteSpace(UserAns); CorrAns = stripWhiteSpace(CorrAns); if (!ok2Continue) return null; var isSpecResp=false; for ( var i=0; i$/.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,indepVars,oComp) { ok2Continue = true; if (!ProcessIt) return null; var fieldname = event.target.name; % \end{macrocode} % Modified for multi-letter variables: remove one line, insert two. (2017/08/09) % \begin{macrocode} % var UserAns = event.value; var bSubstVars=(arguments.length>7); // dps17 var UserAns=(bSubstVars)?arguments[7]:event.value; UserAns = stripWhiteSpace(UserAns); CorrAns = stripWhiteSpace(CorrAns); if (!ok2Continue) return null; var isSpecResp=false; for ( var i=0; i7); // dps17 var UserAns=(bSubstVars)?arguments[7]:event.value; UserAns = stripWhiteSpace(UserAns); CorrAns = stripWhiteSpace(CorrAns); if (!ok2Continue) return null; var isSpecResp=false; for ( var i=0; i7); // dps17 var UserAns=(bSubstVars)?arguments[7]:event.value; UserAns = stripWhiteSpace(UserAns); CorrAns = stripWhiteSpace(CorrAns); if (!ok2Continue) return null; var isSpecResp=false; for ( var i=0; i7); // dps17 var UserAns=(bSubstVars)?arguments[7]:event.value; CorrAns = stripWhiteSpace (CorrAns); UserAns = stripWhiteSpace (UserAns); // sets ok2Continue if ( !ok2Continue ) return null; indepVars+="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()} 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) { 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); indepVars = TypeParameters(indepVars); if (!ok2Continue) return null; success=randomPointCompare( n,domain,indepVars,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,indepVars,oComp) { ok2Continue = true; if (!ProcessIt) return null; var fieldname = event.target.name; % \end{macrocode} % Modified for multi-letter variables: remove one line, insert two. (2017/08/09) % \begin{macrocode} % var UserAns = event.value; var bSubstVars=(arguments.length>7); // dps17 var UserAns=(bSubstVars)?arguments[7]:event.value; UserAns = stripWhiteSpace(UserAns); CorrAns = stripWhiteSpace(CorrAns); // sets ok2Continue if ( !ok2Continue ) return null; var isSpecResp=false; for ( var i=0; i7); // dps17 var UserAns=(bSubstVars)?arguments[7]:event.value; UserAns = stripWhiteSpace(UserAns); CorrAns = stripWhiteSpace(CorrAns); // sets ok2Continue if ( !ok2Continue ) return null; var isSpecResp=false; for ( var i=0; i7)?arguments[7]:event.value; var retn=_ProcRespEvalEq(true,flag,CorrAns,n,epsilon,% indepVars,UserAns); 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,indepVars,oComp) { % \end{macrocode} % Modified for multi-letter variables: remove one line, insert two. (2017/08/09) % \begin{macrocode} var UserAns=(arguments.length>7)?arguments[7]:event.value; var retn=_ProcRespEvalEq(false,flag,CorrAns,n,epsilon, indepVars,UserAns); return retn; } % \end{macrocode} % \begin{macrocode} function _ProcRespEvalEq(allowzero,flag,CorrAns,n,epsilon,indepVars) { ok2Continue = true; if (!ProcessIt) return null; var fieldname = event.target.name; % \end{macrocode} % Modified for multi-letter variables: remove one line, insert two. (2017/08/09) % \begin{macrocode} % var UserAns = event.value; var bSubstVars=(arguments.length>6); // dps17 var UserAns=(bSubstVars)?arguments[6]: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(indepVars); 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,% indepVars,oComp) { % \end{macrocode} % Modified for multi-letter variables: remove one line, insert two. (2017/08/09) % \begin{macrocode} var UserAns=(arguments.length>7)?arguments[7]:event.value; var retn=_ProcRespEvalEqList(true,flag,CorrAns,n,epsilon,% indepVars,UserAns); return retn; } function ProcRespEvalEqListNonZero(flag,CorrAns,n,epsilon,domain,% indepVars,oComp){ % \end{macrocode} % Modified for multi-letter variables: remove one line, insert two. (2017/08/09) % \begin{macrocode} % dps17 var UserAns=(arguments.length>7)?arguments[7]:event.value; var retn=_ProcRespEvalEqList(false,flag,CorrAns,n,epsilon,% indepVars,UserAns); return retn; } function _ProcRespEvalEqList(allowzero,flag,CorrAns,n,epsilon,indepVars) { ok2Continue = true; if (!ProcessIt) return null; var fieldname = event.target.name; % \end{macrocode} % Modified for multi-letter variables: remove one line, insert two. (2017/08/09) % \begin{macrocode} % var UserAns = event.value; var bSubstVars=(arguments.length>6); // dps17 var UserAns=(bSubstVars)?arguments[6]: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(indepVars); 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; i7); var UserAns=(bSubstVars)?arguments[7]: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,indepVars,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,indepVars,oComp) { ok2Continue = true; CorrAns = ParseInput(CorrAns); if (!ok2Continue) { eqAppAlert(\SyntaxErrorAuthor,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 ( (!warnDecDeOnly) && (reDec.test(UserAns)) ) { eqAppAlert(\noDecPtDeMsg,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 ( (!warnDecDeOnly) && (retn==null) ) return -1; } % var reComma=/,/; % if ( reComma.test(UserAns) ) { % eqAppAlert(\eqSyntaxErrorComma,3); % return -1; % } UserAns = ParseInput(UserAns); indepVars = TypeParameters(indepVars); if (!ok2Continue) return null; var success=randomPointCompare(n,domain,indepVars,% 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. % % \subsubsection{\texttt{useEnNums}} % \leavevmode % \IndexOpt{useEnNums}A simple \texttt{ProcResp()} for processing numbers entered in a EN-U format: 1234.56. % \begin{macrocode} \@ifcheckedout{useEnNums} % \end{macrocode} %\leavevmode\DescribeMacro{\noDecPtEnMsg} A general alert message for responses that % contain a comma (,). % \begin{macrocode} \dlJSStr[noquotes]{\noDecPtEnMsg}{% "Syntax Error: A comma (,) was found in your response \"" + UserAns + "\". Please remove the comma, or this answer will be marked as wrong."} % \end{macrocode} % \DescribeMacro{\MsgEni} An alert message when German decimal notation % is expected. % \begin{macrocode} \flJSStr*[noquotes]{\MsgEni}{"English decimal notation is expected, for example: 12.3456."} % \end{macrocode} % \DescribeMacro{\MsgEnii} An alert message when two decimal places are expected. % \begin{macrocode} \flJSStr*[noquotes]{\MsgEnii}{"A decimal number is required, rounded to two decimal places, for example: 12.34"} % \end{macrocode} % \DescribeMacro{\numEn}\nmpsep{\darg{\ameta{Msg}}} is a command that supports % the En decimal notation point (.). For this command, the decimal point (.) % is optional; that is, an integer response is accepted. \ameta{Msg} is a message % that appears on error of syntax. % \begin{macrocode} \def\numEn#1{{priorParse:\Array(% \preReqForm(/\rebstr\rechrclass{+-}?\redigit*\\.?% \redigit*\reestr/,(#1)))}} % \end{macrocode} % \DescribeMacro{\rndNumEnReq}\nmpsep{\darg{\ameta{nDec-pl}}\darg{\ameta{Msg}}} % For numbers that are required to be rounded to \emph{exactly} \ameta{nDec-pl} decimal places. % \ameta{Msg} is an error message. Decimal point (.) is required. % \begin{macrocode} \def\rndNumEnReq#1#2{{priorParse:\Array(% \preReqForm(/\rebstr\rechrclass{+-}?\redigit*\\.% \redigit{#1}\reestr/,(#2)))}} % \end{macrocode} % \DescribeMacro{\rndNumEnOpt}\nmpsep{\darg{\ameta{nDec-pl}}\darg{\ameta{Msg}}} % For numbers that are required to be rounded to \emph{at most} \ameta{nDec-pl} decimal places. % \ameta{Msg} is an error message. Decimal point (.) optional (integer response % accepted). % \begin{macrocode} \def\rndNumEnOpt#1#2{{priorParse:\Array(% at most two decimal places \preReqForm(/\rebstr\rechrclass{+-}?% \redigit*\\.?\redigit{0,#1}\reestr/,(#2)))}} \fi % \end{macrocode} % \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,indepVars,oComp) { ok2Continue = true; if (!ProcessIt) return null; var fieldname = event.target.name; % \end{macrocode} % Modified for multi-letter variables: remove one line, insert two. (2017/08/09) % \begin{macrocode} % var UserAns = event.value; var bSubstVars=(arguments.length>7); // dps17 var UserAns=(bSubstVars)?arguments[7]:event.value; UserAns = stripWhiteSpace(UserAns); if (!ok2Continue) return null; var retn = _ProcResp(flag,CorrAns,UserAns,n,epsilon,% domain,indepVars,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,indepVars,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,indepVars,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,indepVars,oComp) { if (!ProcessIt) return null; ok2Continue = true; var i, success, truthCnt=1; var aScalar, scalar = 1; var fieldname = event.target.name; % \end{macrocode} % Modified for multi-letter variables: remove one line, insert two. (2017/08/09) % \begin{macrocode} % var UserAns = event.value; var bSubstVars=(arguments.length>7); // dps17 var UserAns=(bSubstVars)?arguments[7]:event.value; UserAns = stripWhiteSpace (UserAns); // sets ok2Continue CorrAns = stripWhiteSpace(CorrAns); if ( !ok2Continue ) return null; var isSpecResp=false; for ( var i=0; i7); // dps17 var UserAns=(bSubstVars)?arguments[7]:event.value; UserAns = stripWhiteSpace(UserAns); CorrAns = stripWhiteSpace(CorrAns); if (!ok2Continue) return null; UserAns = UserAns.replace(/inf/g, "x"); CorrAns = CorrAns.replace(/inf/g, "x"); indepVars = "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,indepVars,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,indepVars,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= 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 % \end{macrocode} \endinput