From: jj v. a. <we...@ma...> - 2005-08-25 18:23:43
|
Log Message: ----------- A separate copy of this file is no longer needed. Removed Files: ------------- pg/lib/Parser/Legacy: PGanswermacros.pl Revision Data ------------- --- lib/Parser/Legacy/PGanswermacros.pl +++ /dev/null @@ -1,4851 +0,0 @@ -# This file is PGanswermacros.pl -# This includes the subroutines for the ANS macros, that -# is, macros allowing a more flexible answer checking -#################################################################### -# Copyright @ 1995-2000 University of Rochester -# All Rights Reserved -#################################################################### -#$Id: PGanswermacros.pl,v 1.11 2005/08/24 11:44:49 dpvc Exp $ - -=head1 NAME - - PGanswermacros.pl -- located in the courseScripts directory - -=head1 SYNPOSIS - - Number Answer Evaluators: - num_cmp() -- uses an input hash to determine parameters - - std_num_cmp(), std_num_cmp_list(), std_num_cmp_abs, std_num_cmp_abs_list() - frac_num_cmp(), frac_num_cmp_list(), frac_num_cmp_abs, frac_num_cmp_abs_list() - arith_num_cmp(), arith_num_cmp_list(), arith_num_cmp_abs, arith_num_cmp_abs_list() - strict_num_cmp(), strict_num_cmp_list(), strict_num_cmp_abs, strict_num_cmp_abs_list() - numerical_compare_with_units() -- requires units as part of the answer - std_num_str_cmp() -- also accepts a set of strings as possible answers - - Function Answer Evaluators: - fun_cmp() -- uses an input hash to determine parameters - - function_cmp(), function_cmp_abs() - function_cmp_up_to_constant(), function_cmp_up_to_constant_abs() - multivar_function_cmp() - - String Answer Evaluators: - str_cmp() -- uses an input hash to determine parameters - - std_str_cmp(), std_str_cmp_list(), std_cs_str_cmp(), std_cs_str_cmp_list() - strict_str_cmp(), strict_str_cmp_list() - ordered_str_cmp(), ordered_str_cmp_list(), ordered_cs_str_cmp(), ordered_cs_str_cmp_list() - unordered_str_cmp(), unordered_str_cmp_list(), unordered_cs_str_cmp(), unordered_cs_str_cmp_list() - - Miscellaneous Answer Evaluators: - checkbox_cmp() - radio_cmp() - -=cut - -=head1 DESCRIPTION - -This file adds subroutines which create "answer evaluators" for checking -answers. Each answer evaluator accepts a single input from a student answer, -checks it and creates an output hash %ans_hash with seven or eight entries -(the preview_latex_string is optional). The output hash is now being created -with the AnswerHash package "class", which is located at the end of this file. -This class is currently just a wrapper for the hash, but this might change in -the future as new capabilities are added. - - score => $correctQ, - correct_ans => $originalCorrEqn, - student_ans => $modified_student_ans - original_student_ans => $original_student_answer, - ans_message => $PGanswerMessage, - type => 'typeString', - preview_text_string => $preview_text_string, - preview_latex_string => $preview_latex_string - - - $ans_hash{score} -- a number between 0 and 1 indicating - whether the answer is correct. Fractions - allow the implementation of partial - credit for incorrect answers. - $ans_hash{correct_ans} -- The correct answer, as supplied by the - instructor and then formatted. This can - be viewed by the student after the answer date. - $ans_hash{student_ans} -- This is the student answer, after reformatting; - for example the answer might be forced - to capital letters for comparison with - the instructors answer. For a numerical - answer, it gives the evaluated answer. - This is displayed in the section reporting - the results of checking the student answers. - $ans_hash{original_student_ans} -- This is the original student answer. This is displayed - on the preview page and may be used for sticky answers. - $ans_hash{ans_message} -- Any error message, or hint provided by the answer evaluator. - This is also displayed in the section reporting - the results of checking the student answers. - $ans_hash{type} -- A string indicating the type of answer evaluator. This - helps in preprocessing the student answer for errors. - Some examples: - 'number_with_units' - 'function' - 'frac_number' - 'arith_number' - $ans_hash{preview_text_string} -- This typically shows how the student answer was parsed. It is - displayed on the preview page. For a student answer of 2sin(3x) - this would be 2*sin(3*x). For string answers it is typically the - same as $ans_hash{student_ans}. - $ans_hash{preview_latex_string} -- THIS IS OPTIONAL. This is latex version of the student answer - which is used to show a typeset view on the answer on the preview - page. For a student answer of 2/3, this would be \frac{2}{3}. - -Technical note: the routines in this file are not actually answer evaluators. Instead, they create -answer evaluators. An answer evaluator is an anonymous subroutine, referenced by a named scalar. The -routines in this file build the subroutine and return a reference to it. Later, when the student -actually enters an answer, the problem processor feeds that answer to the referenced subroutine, which -evaluates it and returns a score (usually 0 or 1). For most users, this distinction is unimportant, but -if you plan on writing your own answer evaluators, you should understand this point. - -=cut - -BEGIN { - be_strict(); # an alias for use strict. This means that all global variable must contain main:: as a prefix. -} - - -my ($BR , # convenient localizations. - $PAR , - $numRelPercentTolDefault , - $numZeroLevelDefault , - $numZeroLevelTolDefault , - $numAbsTolDefault , - $numFormatDefault , - $functRelPercentTolDefault , - $functZeroLevelDefault , - $functZeroLevelTolDefault , - $functAbsTolDefault , - $functNumOfPoints , - $functVarDefault , - $functLLimitDefault , - $functULimitDefault , - $functMaxConstantOfIntegration , - $CA , - $rh_envir , - $useBaseTenLog , - $inputs_ref , - $QUESTIONNAIRE_ANSWERS , - $user_context, - $Context, -); - - - - -sub _PGanswermacros_init { - - $BR = main::PG_restricted_eval(q!$main::BR!); - $PAR = main::PG_restricted_eval(q!$main::PAR!); - - # import defaults - # these are now imported from the %envir variable - $numRelPercentTolDefault = main::PG_restricted_eval(q!$main::numRelPercentTolDefault!); - $numZeroLevelDefault = main::PG_restricted_eval(q!$main::numZeroLevelDefault!); - $numZeroLevelTolDefault = main::PG_restricted_eval(q!$main::numZeroLevelTolDefault!); - $numAbsTolDefault = main::PG_restricted_eval(q!$main::numAbsTolDefault!); - $numFormatDefault = main::PG_restricted_eval(q!$main::numFormatDefault!); - $functRelPercentTolDefault = main::PG_restricted_eval(q!$main::functRelPercentTolDefault!); - $functZeroLevelDefault = main::PG_restricted_eval(q!$main::functZeroLevelDefault!); - $functZeroLevelTolDefault = main::PG_restricted_eval(q!$main::functZeroLevelTolDefault!); - $functAbsTolDefault = main::PG_restricted_eval(q!$main::functAbsTolDefault!); - $functNumOfPoints = main::PG_restricted_eval(q!$main::functNumOfPoints!); - $functVarDefault = main::PG_restricted_eval(q!$main::functVarDefault!); - $functLLimitDefault = main::PG_restricted_eval(q!$main::functLLimitDefault!); - $functULimitDefault = main::PG_restricted_eval(q!$main::functULimitDefault!); - $functMaxConstantOfIntegration = main::PG_restricted_eval(q!$main::functMaxConstantOfIntegration!); - $rh_envir = main::PG_restricted_eval(q!\%main::envir!); - $useBaseTenLog = main::PG_restricted_eval(q!$main::useBaseTenLog!); - $inputs_ref = main::PG_restricted_eval(q!$main::inputs_ref!); - $QUESTIONNAIRE_ANSWERS = ''; - - if (!main::PG_restricted_eval(q!$main::useOldAnswerMacros!)) { - $user_context = main::PG_restricted_eval(q!\%context!); - $Context = sub {Parser::Context->current($user_context,@_)}; - } -} - - - -########################################################################## - -#Note use $rh_envir to read environment variables - -########################################################################## -## Number answer evaluators - -=head2 Number Answer Evaluators - -Number answer evaluators take in a numerical answer, compare it to the correct answer, -and return a score. In addition, they can choose to accept or reject an answer based on -its format, closeness to the correct answer, and other criteria. There are two types -of numerical answer evaluators: num_cmp(), which takes a hash of named options as parameters, -and the "mode"_num_cmp() variety, which use different functions to access different sets of -options. In addition, there is the special case of std_num_str_cmp(), which can evaluate -both numbers and strings. - -Numerical Comparison Options - - correctAnswer -- This is the correct answer that the student answer will - be compared to. However, this does not mean that the - student answer must match this exactly. How close the - student answer must be is determined by the other - options, especially tolerance and format. - - tolerance -- These options determine how close the student answer - must be to the correct answer to qualify. There are two - types of tolerance: relative and absolute. Relative - tolerances are given in percentages. A relative - tolerance of 1 indicates that the student answer must - be within 1% of the correct answer to qualify as correct. - In other words, a student answer is correct when - abs(studentAnswer - correctAnswer) <= abs(.01*relpercentTol*correctAnswer) - Using absolute tolerance, the student answer must be a - fixed distance from the correct answer to qualify. - For example, an absolute tolerance of 5 means that any - number which is +-5 of the correct answer qualifies as correct. - Final (rarely used) tolerance options are zeroLevel - and zeroLevelTol, used in conjunction with relative - tolerance. if correctAnswer has absolute value less than - or equal to zeroLevel, then the student answer must be, - in absolute terms, within zeroLevelTol of correctAnswer, i.e., - abs(studentAnswer - correctAnswer) <= zeroLevelTol. - In other words, if the correct answer is very near zero, - an absolute tolerance will be used. One must do this to - handle floating point answers very near zero, because of - the inaccuracy of floating point arithmetic. However, the - default values are almost always adequate. - - mode -- This determines the allowable methods for entering an - answer. Answers which do not meet this requirement will - be graded as incorrect, regardless of their numerical - value. The recognized modes are: - 'std' (default) -- allows any expression which evaluates - to a number, including those using - elementary functions like sin() and - exp(), as well as the operations of - arithmetic (+, -, *, /, ^) - 'strict' -- only decimal numbers are allowed - 'frac' -- whole numbers and fractions are allowed - 'arith' -- arithmetic expressions are allowed, but - no functions - Note that all modes allow the use of "pi" and "e" as - constants, and also the use of "E" to represent scientific - notation. - - format -- The format to use when displaying the correct and - submitted answers. This has no effect on how answers are - evaluated; it is only for cosmetic purposes. The - formatting syntax is the same as Perl uses for the sprintf() - function. Format strings are of the form '%m.nx' or '%m.nx#', - where m and n are described below, and x is a formatter. - Esentially, m is the minimum length of the field - (make this negative to left-justify). Note that the decimal - point counts as a character when determining the field width. - If m begins with a zero, the number will be padded with zeros - instead of spaces to fit the field. - The precision specifier (n) works differently, depending - on which formatter you are using. For d, i, o, u, x and X - formatters (non-floating point formatters), n is the minimum - number of digits to display. For e and f, it is the number of - digits that appear after the decimal point (extra digits will - be rounded; insufficient digits will be padded with spaces--see - '#' below). For g, it is the number of significant digits to - display. - The full list of formatters can be found in the manpage - for printf(3), or by typing "perldoc -f sprintf" at a - terminal prompt. The following is a brief summary of the - most frequent formatters: - d -- decimal number - ld -- long decimal number - u -- unsigned decimal number - lu -- long unsigned decimal number - x -- hexadecimal number - o -- octal number - e -- floating point number in scientific notation - f -- floating point number - g -- either e or f, whichever takes less space - Technically, g will use e if the exponent is less than -4 or - greater than or equal to the precision. Trailing zeros are - removed in this mode. - If the format string ends in '#', trailing zeros will be - removed in the decimal part. Note that this is not a standard - syntax; it is handled internally by WeBWorK and not by Perl - (although this should not be a concern to end users). - The default format is '%0.5f#', which displays as a floating - point number with 5 digits of precision and no trailing zeros. - Other useful format strings might be '%0.2f' for displaying - dollar amounts, or '%010d' to display an integer with leading - zeros. Setting format to an empty string ( '' ) means no - formatting will be used; this will show 'arbitrary' precision - floating points. - -Default Values (As of 7/24/2000) (Option -- Variable Name -- Value) - - Format -- $numFormatDefault -- "%0.5f#" - Relative Tolerance -- $numRelPercentTolDefault -- .1 - Absolute Tolerance -- $numAbsTolDefault -- .001 - Zero Level -- $numZeroLevelDefault -- 1E-14 - Zero Level Tolerance -- $numZeroLevelTolDefault -- 1E-12 - -=cut - - -=head3 num_cmp() - -Compares a number or a list of numbers, using a named hash of options to set -parameters. This can make for more readable code than using the "mode"_num_cmp() -style, but some people find one or the other easier to remember. - -ANS( num_cmp( answer or answer_array_ref, options_hash ) ); - - 1. the correct answer, or a reference to an array of correct answers - 2. a hash with the following keys (all optional): - mode -- 'std' (default) (allows any expression evaluating to - a number) - 'strict' (only numbers are allowed) - 'frac' (fractions are allowed) - 'arith' (arithmetic expressions allowed) - format -- '%0.5f#' (default); defines formatting for the - correct answer - tol -- an absolute tolerance, or - relTol -- a relative tolerance - units -- the units to use for the answer(s) - strings -- a reference to an array of strings which are valid - answers (works like std_num_str_cmp() ) - zeroLevel -- if the correct answer is this close to zero, - then zeroLevelTol applies - zeroLevelTol -- absolute tolerance to allow when answer is close - to zero - - debug -- if set to 1, provides verbose listing of - hash entries throughout fliters. - - Returns an answer evaluator, or (if given a reference to an array of - answers), a list of answer evaluators. Note that a reference to an array of - answers results is just a shortcut for writing a separate <code>num_cmp()</code> for each - answer. - -EXAMPLES: - - num_cmp( 5 ) -- correct answer is 5, using defaults - for all options - num_cmp( [5,6,7] ) -- correct answers are 5, 6, and 7, - using defaults for all options - num_cmp( 5, mode => 'strict' ) -- correct answer is 5, mode is strict - num_cmp( [5,6], relTol => 5 ) -- correct answers are 5 and 6, - both with 5% relative tolerance - num_cmp( 6, strings => ["Inf", "Minf", "NaN"] ) - -- correct answer is 6, "Inf", "Minf", - and "NaN" recognized as valid, but - incorrect answers. - num_cmp( "-INF", strings => ["INF", "-INF"] ) - -- correct answer is "-INF", "INF" and - numerical expressions recognized as valid, - but incorrect answers. - - -=cut - -sub num_cmp { - my $correctAnswer = shift @_; - $CA = $correctAnswer; - my @opt = @_; - my %out_options; - -######################################################################### -# Retain this first check for backword compatibility. Allows input of the form -# num_cmp($ans, 1, '%0.5f') but warns against it -######################################################################### - my %known_options = ( - 'mode' => 'std', - 'format' => $numFormatDefault, - 'tol' => $numAbsTolDefault, - 'relTol' => $numRelPercentTolDefault, - 'units' => undef, - 'strings' => undef, - 'zeroLevel' => $numZeroLevelDefault, - 'zeroLevelTol' => $numZeroLevelTolDefault, - 'tolType' => 'relative', - 'tolerance' => 1, - 'reltol' => undef, #alternate spelling - 'unit' => undef, #alternate spelling - 'debug' => 0 - ); - - my @output_list; - my( $relPercentTol, $format, $zeroLevel, $zeroLevelTol) = @opt; - - unless( ref($correctAnswer) eq 'ARRAY' || scalar( @opt ) == 0 || - ( defined($opt[0]) and exists $known_options{$opt[0]} ) ) { - # unless the first parameter is a list of arrays - # or the second parameter is a known option or - # no options were used, - # use the old num_cmp which does not use options, but has inputs - # $relPercentTol,$format,$zeroLevel,$zeroLevelTol - warn "This method of using num_cmp() is deprecated. Please rewrite this" . - " problem using the options style of parameter passing (or" . - " check that your first option is spelled correctly)."; - - %out_options = ( 'relTol' => $relPercentTol, - 'format' => $format, - 'zeroLevel' => $zeroLevel, - 'zeroLevelTol' => $zeroLevelTol, - 'mode' => 'std' - ); - } - -######################################################################### -# Now handle the options assuming they are entered in the form -# num_cmp($ans, relTol=>1, format=>'%0.5f') -######################################################################### - %out_options = @opt; - assign_option_aliases( \%out_options, - 'reltol' => 'relTol', - 'unit' => 'units', - 'abstol' => 'tol', - ); - - set_default_options( \%out_options, - 'tolType' => (defined($out_options{'tol'}) ) ? 'absolute' : 'relative', # the existence of "tol" means that we use absolute tolerance mode - 'tolerance' => (defined($out_options{'tolType'}) && $out_options{'tolType'} eq 'absolute' ) ? $numAbsTolDefault : $numRelPercentTolDefault, # relative tolerance is the default - 'mode' => 'std', - 'format' => $numFormatDefault, - 'tol' => undef, - 'relTol' => undef, - 'units' => undef, - 'strings' => undef, - 'zeroLevel' => $numZeroLevelDefault, - 'zeroLevelTol' => $numZeroLevelTolDefault, - 'debug' => 0, - ); - - # can't use both units and strings - if( defined( $out_options{'units'} ) && defined( $out_options{'strings'} ) ) { - warn "Can't use both 'units' and 'strings' in the same problem " . - "(check your parameters to num_cmp() )"; - } - - # absolute tolType and relTol are incompatible. So are relative tolType and tol - if( defined( $out_options{'relTol'} ) && $out_options{'tolType'} eq 'absolute' ) { - warn "The 'tolType' 'absolute' is not compatible with 'relTol' " . - "(check your parameters to num_cmp() )"; - } - if( defined( $out_options{'tol'} ) && $out_options{'tolType'} eq 'relative' ) { - warn "The 'tolType' 'relative' is not compatible with 'tol' " . - "(check your parameters to num_cmp() )"; - } - - - # Handle legacy options - if ($out_options{tolType} eq 'absolute') { - $out_options{'tolerance'}=$out_options{'tol'} if defined($out_options{'tol'}); - delete($out_options{'relTol'}) if exists( $out_options{'relTol'} ); - } else { - $out_options{'tolerance'}=$out_options{'relTol'} if defined($out_options{'relTol'}); - # delete($out_options{'tol'}) if exists( $out_options{'tol'} ); - } - # end legacy options - - # thread over lists - my @ans_list = (); - - if ( ref($correctAnswer) eq 'ARRAY' ) { - @ans_list = @{$correctAnswer}; - } - else { push( @ans_list, $correctAnswer ); - } - - # produce answer evaluators - foreach my $ans (@ans_list) { - if( defined( $out_options{'units'} ) ) { - $ans = "$ans $out_options{'units'}"; - - push( @output_list, NUM_CMP( 'correctAnswer' => $ans, - 'tolerance' => $out_options{'tolerance'}, - 'tolType' => $out_options{'tolType'}, - 'format' => $out_options{'format'}, - 'mode' => $out_options{'mode'}, - 'zeroLevel' => $out_options{'zeroLevel'}, - 'zeroLevelTol' => $out_options{'zeroLevelTol'}, - 'debug' => $out_options{'debug'}, - 'units' => $out_options{'units'}, - ) - ); - } elsif( defined( $out_options{'strings'} ) ) { - - - push( @output_list, NUM_CMP( 'correctAnswer' => $ans, - 'tolerance' => $out_options{tolerance}, - 'tolType' => $out_options{tolType}, - 'format' => $out_options{'format'}, - 'mode' => $out_options{'mode'}, - 'zeroLevel' => $out_options{'zeroLevel'}, - 'zeroLevelTol' => $out_options{'zeroLevelTol'}, - 'debug' => $out_options{'debug'}, - 'strings' => $out_options{'strings'}, - ) - ); - } else { - push(@output_list, - NUM_CMP( 'correctAnswer' => $ans, - 'tolerance' => $out_options{tolerance}, - 'tolType' => $out_options{tolType}, - 'format' => $out_options{'format'}, - 'mode' => $out_options{'mode'}, - 'zeroLevel' => $out_options{'zeroLevel'}, - 'zeroLevelTol' => $out_options{'zeroLevelTol'}, - 'debug' => $out_options{'debug'}, - ), - ); - } - } - - return (wantarray) ? @output_list : $output_list[0]; -} - -#legacy code for compatability purposes -sub num_rel_cmp { # compare numbers - std_num_cmp( @_ ); -} - - -=head3 "mode"_num_cmp() functions - -There are 16 functions total, 4 for each mode (std, frac, strict, arith). Each mode has -one "normal" function, one which accepts a list of answers, one which uses absolute -rather than relative tolerance, and one which uses absolute tolerance and accepts a list. -The "std" family is documented below; all others work precisely the same. - - std_num_cmp($correctAnswer) OR - std_num_cmp($correctAnswer, $relPercentTol) OR - std_num_cmp($correctAnswer, $relPercentTol, $format) OR - std_num_cmp($correctAnswer, $relPercentTol, $format, $zeroLevel) OR - std_num_cmp($correctAnswer, $relPercentTol, $format, $zeroLevel, $zeroLevelTol) - - $correctAnswer -- the correct answer - $relPercentTol -- the tolerance, as a percentage (optional) - $format -- the format of the displayed answer (optional) - $zeroLevel -- if the correct answer is this close to zero, then zeroLevelTol applies (optional) - $zeroLevelTol -- absolute tolerance to allow when correct answer is close to zero (optional) - - std_num_cmp() uses standard mode (arithmetic operations and elementary - functions allowed) and relative tolerance. Options are specified by - one or more parameters. Note that if you wish to set an option which - is later in the parameter list, you must set all previous options. - - std_num_cmp_abs($correctAnswer) OR - std_num_cmp_abs($correctAnswer, $absTol) OR - std_num_cmp_abs($correctAnswer, $absTol, $format) - - $correctAnswer -- the correct answer - $absTol -- an absolute tolerance (optional) - $format -- the format of the displayed answer (optional) - - std_num_cmp_abs() uses standard mode and absolute tolerance. Options - are set as with std_num_cmp(). Note that $zeroLevel and $zeroLevelTol - do not apply with absolute tolerance. - - std_num_cmp_list($relPercentTol, $format, @answerList) - - $relPercentTol -- the tolerance, as a percentage - $format -- the format of the displayed answer(s) - @answerList -- a list of one or more correct answers - - std_num_cmp_list() uses standard mode and relative tolerance. There - is no way to set $zeroLevel or $zeroLevelTol. Note that no - parameters are optional. All answers in the list will be - evaluated with the same set of parameters. - - std_num_cmp_abs_list($absTol, $format, @answerList) - - $absTol -- an absolute tolerance - $format -- the format of the displayed answer(s) - @answerList -- a list of one or more correct answers - - std_num_cmp_abs_list() uses standard mode and absolute tolerance. - Note that no parameters are optional. All answers in the list will be - evaluated with the same set of parameters. - - arith_num_cmp(), arith_num_cmp_list(), arith_num_cmp_abs(), arith_num_cmp_abs_list() - strict_num_cmp(), strict_num_cmp_list(), strict_num_cmp_abs(), strict_num_cmp_abs_list() - frac_num_cmp(), frac_num_cmp_list(), frac_num_cmp_abs(), frac_num_cmp_abs_list() - -Examples: - - ANS( strict_num_cmp( 3.14159 ) ) -- The student answer must be a number - in decimal or scientific notation which is within .1 percent of 3.14159. - This assumes $numRelPercentTolDefault has been set to .1. - ANS( strict_num_cmp( $answer, .01 ) ) -- The student answer must be a - number within .01 percent of $answer (e.g. 3.14159 if $answer is 3.14159 - or $answer is "pi" or $answer is 4*atan(1)). - ANS( frac_num_cmp( $answer) ) or ANS( frac_num_cmp( $answer,.01 )) -- - The student answer can be a number or fraction, e.g. 2/3. - ANS( arith_num_cmp( $answer) ) or ANS( arith_num_cmp( $answer,.01 )) -- - The student answer can be an arithmetic expression, e.g. (2+3)/7-2^.5 . - ANS( std_num_cmp( $answer) ) or ANS( std_num_cmp( $answer,.01 )) -- - The student answer can contain elementary functions, e.g. sin(.3+pi/2) - -=cut - -sub std_num_cmp { # compare numbers allowing use of elementary functions - my ( $correctAnswer, $relPercentTol, $format, $zeroLevel, $zeroLevelTol ) = @_; - - my %options = ( 'relTol' => $relPercentTol, - 'format' => $format, - 'zeroLevel' => $zeroLevel, - 'zeroLevelTol' => $zeroLevelTol - ); - - set_default_options( \%options, - 'tolType' => 'relative', - 'tolerance' => $numRelPercentTolDefault, - 'mode' => 'std', - 'format' => $numFormatDefault, - 'relTol' => $numRelPercentTolDefault, - 'zeroLevel' => $numZeroLevelDefault, - 'zeroLevelTol' => $numZeroLevelTolDefault, - 'debug' => 0, - ); - - num_cmp([$correctAnswer], %options); -} - -## Similar to std_num_cmp but accepts a list of numbers in the form -## std_num_cmp_list(relpercentTol,format,ans1,ans2,ans3,...) -## format is of the form "%10.3g" or "", i.e., a format suitable for sprintf(). Use "" for default -## You must enter a format and tolerance - -sub std_num_cmp_list { - my ( $relPercentTol, $format, @answerList) = @_; - - my %options = ( 'relTol' => $relPercentTol, - 'format' => $format, - ); - - set_default_options( \%options, - 'tolType' => 'relative', - 'tolerance' => $numRelPercentTolDefault, - 'mode' => 'std', - 'format' => $numFormatDefault, - 'relTol' => $numRelPercentTolDefault, - 'zeroLevel' => $numZeroLevelDefault, - 'zeroLevelTol' => $numZeroLevelTolDefault, - 'debug' => 0, - ); - - num_cmp(\@answerList, %options); - -} - -sub std_num_cmp_abs { # compare numbers allowing use of elementary functions with absolute tolerance - my ( $correctAnswer, $absTol, $format) = @_; - my %options = ( 'tolerance' => $absTol, - 'format' => $format - ); - - set_default_options (\%options, - 'tolType' => 'absolute', - 'tolerance' => $absTol, - 'mode' => 'std', - 'format' => $numFormatDefault, - 'zeroLevel' => 0, - 'zeroLevelTol' => 0, - 'debug' => 0, - ); - - num_cmp([$correctAnswer], %options); -} - -## See std_num_cmp_list for usage - -sub std_num_cmp_abs_list { - my ( $absTol, $format, @answerList ) = @_; - - my %options = ( 'tolerance' => $absTol, - 'format' => $format, - ); - - set_default_options( \%options, - 'tolType' => 'absolute', - 'tolerance' => $absTol, - 'mode' => 'std', - 'format' => $numFormatDefault, - 'zeroLevel' => 0, - 'zeroLevelTol' => 0, - 'debug' => 0, - ); - - num_cmp(\@answerList, %options); -} - -sub frac_num_cmp { # only allow fractions and numbers as submitted answer - - my ( $correctAnswer, $relPercentTol, $format, $zeroLevel, $zeroLevelTol ) = @_; - - my %options = ( 'relTol' => $relPercentTol, - 'format' => $format, - 'zeroLevel' => $zeroLevel, - 'zeroLevelTol' => $zeroLevelTol - ); - - set_default_options( \%options, - 'tolType' => 'relative', - 'tolerance' => $relPercentTol, - 'mode' => 'frac', - 'format' => $numFormatDefault, - 'zeroLevel' => $numZeroLevelDefault, - 'zeroLevelTol' => $numZeroLevelTolDefault, - 'relTol' => $numRelPercentTolDefault, - 'debug' => 0, - ); - - num_cmp([$correctAnswer], %options); -} - -## See std_num_cmp_list for usage -sub frac_num_cmp_list { - my ( $relPercentTol, $format, @answerList ) = @_; - - my %options = ( 'relTol' => $relPercentTol, - 'format' => $format - ); - - set_default_options( \%options, - 'tolType' => 'relative', - 'tolerance' => $relPercentTol, - 'mode' => 'frac', - 'format' => $numFormatDefault, - 'zeroLevel' => $numZeroLevelDefault, - 'zeroLevelTol' => $numZeroLevelTolDefault, - 'relTol' => $numRelPercentTolDefault, - 'debug' => 0, - ); - - num_cmp(\@answerList, %options); -} - -sub frac_num_cmp_abs { # only allow fraction expressions as submitted answer with absolute tolerance - my ( $correctAnswer, $absTol, $format ) = @_; - - my %options = ( 'tolerance' => $absTol, - 'format' => $format - ); - - set_default_options (\%options, - 'tolType' => 'absolute', - 'tolerance' => $absTol, - 'mode' => 'frac', - 'format' => $numFormatDefault, - 'zeroLevel' => 0, - 'zeroLevelTol' => 0, - 'debug' => 0, - ); - - num_cmp([$correctAnswer], %options); -} - -## See std_num_cmp_list for usage - -sub frac_num_cmp_abs_list { - my ( $absTol, $format, @answerList ) = @_; - - my %options = ( 'tolerance' => $absTol, - 'format' => $format - ); - - set_default_options (\%options, - 'tolType' => 'absolute', - 'tolerance' => $absTol, - 'mode' => 'frac', - 'format' => $numFormatDefault, - 'zeroLevel' => 0, - 'zeroLevelTol' => 0, - 'debug' => 0, - ); - - num_cmp(\@answerList, %options); -} - - -sub arith_num_cmp { # only allow arithmetic expressions as submitted answer - - my ( $correctAnswer, $relPercentTol, $format, $zeroLevel, $zeroLevelTol ) = @_; - - my %options = ( 'relTol' => $relPercentTol, - 'format' => $format, - 'zeroLevel' => $zeroLevel, - 'zeroLevelTol' => $zeroLevelTol - ); - - set_default_options( \%options, - 'tolType' => 'relative', - 'tolerance' => $relPercentTol, - 'mode' => 'arith', - 'format' => $numFormatDefault, - 'zeroLevel' => $numZeroLevelDefault, - 'zeroLevelTol' => $numZeroLevelTolDefault, - 'relTol' => $numRelPercentTolDefault, - 'debug' => 0, - ); - - num_cmp([$correctAnswer], %options); -} - -## See std_num_cmp_list for usage -sub arith_num_cmp_list { - my ( $relPercentTol, $format, @answerList ) = @_; - - my %options = ( 'relTol' => $relPercentTol, - 'format' => $format, - ); - - set_default_options( \%options, - 'tolType' => 'relative', - 'tolerance' => $relPercentTol, - 'mode' => 'arith', - 'format' => $numFormatDefault, - 'zeroLevel' => $numZeroLevelDefault, - 'zeroLevelTol' => $numZeroLevelTolDefault, - 'relTol' => $numRelPercentTolDefault, - 'debug' => 0, - ); - - num_cmp(\@answerList, %options); -} - -sub arith_num_cmp_abs { # only allow arithmetic expressions as submitted answer with absolute tolerance - my ( $correctAnswer, $absTol, $format ) = @_; - - my %options = ( 'tolerance' => $absTol, - 'format' => $format - ); - - set_default_options (\%options, - 'tolType' => 'absolute', - 'tolerance' => $absTol, - 'mode' => 'arith', - 'format' => $numFormatDefault, - 'zeroLevel' => 0, - 'zeroLevelTol' => 0, - 'debug' => 0, - ); - - num_cmp([$correctAnswer], %options); -} - -## See std_num_cmp_list for usage -sub arith_num_cmp_abs_list { - my ( $absTol, $format, @answerList ) = @_; - - my %options = ( 'tolerance' => $absTol, - 'format' => $format - ); - - set_default_options (\%options, - 'tolType' => 'absolute', - 'tolerance' => $absTol, - 'mode' => 'arith', - 'format' => $numFormatDefault, - 'zeroLevel' => 0, - 'zeroLevelTol' => 0, - 'debug' => 0, - ); - - num_cmp(\@answerList, %options); -} - -sub strict_num_cmp { # only allow numbers as submitted answer - my ( $correctAnswer, $relPercentTol, $format, $zeroLevel, $zeroLevelTol ) = @_; - - my %options = ( 'relTol' => $relPercentTol, - 'format' => $format, - 'zeroLevel' => $zeroLevel, - 'zeroLevelTol' => $zeroLevelTol - ); - - set_default_options( \%options, - 'tolType' => 'relative', - 'tolerance' => $relPercentTol, - 'mode' => 'strict', - 'format' => $numFormatDefault, - 'zeroLevel' => $numZeroLevelDefault, - 'zeroLevelTol' => $numZeroLevelTolDefault, - 'relTol' => $numRelPercentTolDefault, - 'debug' => 0, - ); - num_cmp([$correctAnswer], %options); - -} - -## See std_num_cmp_list for usage -sub strict_num_cmp_list { # compare numbers - my ( $relPercentTol, $format, @answerList ) = @_; - - my %options = ( 'relTol' => $relPercentTol, - 'format' => $format, - ); - - set_default_options( \%options, - 'tolType' => 'relative', - 'tolerance' => $relPercentTol, - 'mode' => 'strict', - 'format' => $numFormatDefault, - 'zeroLevel' => $numZeroLevelDefault, - 'zeroLevelTol' => $numZeroLevelTolDefault, - 'relTol' => $numRelPercentTolDefault, - 'debug' => 0, - ); - - num_cmp(\@answerList, %options); -} - - -sub strict_num_cmp_abs { # only allow numbers as submitted answer with absolute tolerance - my ( $correctAnswer, $absTol, $format ) = @_; - - my %options = ( 'tolerance' => $absTol, - 'format' => $format - ); - - set_default_options (\%options, - 'tolType' => 'absolute', - 'tolerance' => $absTol, - 'mode' => 'strict', - 'format' => $numFormatDefault, - 'zeroLevel' => 0, - 'zeroLevelTol' => 0, - 'debug' => 0, - ); - num_cmp([$correctAnswer], %options); - -} - -## See std_num_cmp_list for usage -sub strict_num_cmp_abs_list { # compare numbers - my ( $absTol, $format, @answerList ) = @_; - - my %options = ( 'tolerance' => $absTol, - 'format' => $format - ); - - set_default_options (\%options, - 'tolType' => 'absolute', - 'tolerance' => $absTol, - 'mode' => 'strict', - 'format' => $numFormatDefault, - 'zeroLevel' => 0, - 'zeroLevelTol' => 0, - 'debug' => 0, - ); - - num_cmp(\@answerList, %options); -} - -## sub numerical_compare_with_units -## Compares a number with units -## Deprecated; use num_cmp() -## -## IN: a string which includes the numerical answer and the units -## a hash with the following keys (all optional): -## mode -- 'std', 'frac', 'arith', or 'strict' -## format -- the format to use when displaying the answer -## tol -- an absolute tolerance, or -## relTol -- a relative tolerance -## zeroLevel -- if the correct answer is this close to zero, then zeroLevelTol applies -## zeroLevelTol -- absolute tolerance to allow when correct answer is close to zero - -# This mode is depricated. send input through num_cmp -- it can handle units. - -sub numerical_compare_with_units { - my $correct_answer = shift; # the answer is a string which includes both the numerical answer and the units. - my %options = @_; # all of the other inputs are (key value) pairs - - # Prepare the correct answer - $correct_answer = str_filters( $correct_answer, 'trim_whitespace' ); - - # it surprises me that the match below works since the first .* is greedy. - my ($correct_num_answer, $correct_units) = $correct_answer =~ /^(.*)\s+([^\s]*)$/; - $options{units} = $correct_units; - - num_cmp($correct_num_answer, %options); -} - - -=head3 std_num_str_cmp() - -NOTE: This function is maintained for compatibility. num_cmp() with the - 'strings' parameter is slightly preferred. - -std_num_str_cmp() is used when the correct answer could be either a number or a -string. For example, if you wanted the student to evaluate a function at number -of points, but write "Inf" or "Minf" if the function is unbounded. This routine -will provide error messages that do not give a hint as to whether the correct -answer is a string or a number. For numerical comparisons, std_num_cmp() is -used internally; for string comparisons, std_str_cmp() is used. String answers -must consist entirely of letters except that an initial minus sign is allowed. -E.g. "inf" and "-inf" are valid strings where as "too-big" is not. - - std_num_str_cmp( $correctAnswer ) OR - std_num_str_cmp( $correctAnswer, $ra_legalStrings ) OR - std_num_str_cmp( $correctAnswer, $ra_legalStrings, $relPercentTol ) OR - std_num_str_cmp( $correctAnswer, $ra_legalStrings, $relPercentTol, $format ) OR - std_num_str_cmp( $correctAnswer, $ra_legalStrings, $relPercentTol, $format, $zeroLevel ) OR - std_num_str_cmp( $correctAnswer, $ra_legalStrings, $relPercentTol, $format, - $zeroLevel, $zeroLevelTol ) - - $correctAnswer -- the correct answer - $ra_legalStrings -- a reference to an array of legal strings, e.g. ["str1", "str2"] - $relPercentTol -- the error tolerance as a percentage - $format -- the display format - $zeroLevel -- if the correct answer is this close to zero, then zeroLevelTol applies - $zeroLevelTol -- absolute tolerance to allow when correct answer is close to zero - -Examples: - ANS( std_num_str_cmp( $ans, ["Inf", "Minf", "NaN"] ) ); - ANS( std_num_str_cmp( $ans, ["INF", "-INF"] ) ); - -=cut - -sub std_num_str_cmp { - my ( $correctAnswer, $ra_legalStrings, $relpercentTol, $format, $zeroLevel, $zeroLevelTol ) = @_; - # warn ('This method is depreciated. Use num_cmp instead.'); - return num_cmp ($correctAnswer, strings=>$ra_legalStrings, relTol=>$relpercentTol, format=>$format, - zeroLevel=>$zeroLevel, zeroLevelTol=>$zeroLevelTol); -} - -sub NUM_CMP { # low level numeric compare (now uses Parser) - return ORIGINAL_NUM_CMP(@_) - if main::PG_restricted_eval(q!$main::useOldAnswerMacros!); - - my %num_params = @_; - - # - # check for required parameters - # - my @keys = qw(correctAnswer tolerance tolType format mode zeroLevel zeroLevelTol debug); - foreach my $key (@keys) { - warn "$key must be defined in options when calling NUM_CMP" - unless defined($num_params{$key}); - } - - my $correctAnswer = $num_params{correctAnswer}; - my $mode = $num_params{mode}; - my %options = (debug => $num_params{debug}); - - # - # Hack to fix up exponential notation in correct answer - # (e.g., perl will pass .0000001 as 1e-07). - # - $correctAnswer = uc($correctAnswer) - if $correctAnswer =~ m/e/ && Value::isNumber($correctAnswer); - - # - # Get an apppropriate context based on the mode - # - my $context; - for ($mode) { - /^strict$/i and do { - $context = $Parser::Context::Default::context{LimitedNumeric}->copy; - last; - }; - /^arith$/i and do { - $context = $Parser::Context::Default::context{LegacyNumeric}->copy; - $context->functions->disable('All'); - last; - }; - /^frac$/i and do { - $context = $Parser::Context::Default::context{'LimitedNumeric-Fraction'}->copy; - last; - }; - - # default - $context = $Parser::Context::Default::context{LegacyNumeric}->copy; - } - $context->{format}{number} = $num_params{'format'}; - $context->strings->clear; - # FIXME: should clear variables as well? Copy them from the current context? - - # - # Add the strings to the context - # - if ($num_params{strings}) { - foreach my $string (@{$num_params{strings}}) { - my %tex = ($string =~ m/(-?)inf(inity)?/i)? (TeX => "$1\\infty"): (); - $context->strings->add(uc($string) => {%tex}); - } - } - - # - # Set the tolerances - # - if ($num_params{tolType} eq 'absolute') { - $context->flags->set( - tolerance => $num_params{tolerance}, - tolType => 'absolute', - ); - } else { - $context->flags->set( - tolerance => .01*$num_params{tolerance}, - tolType => 'relative', - ); - } - $context->flags->set( - zeroLevel => $num_params{zeroLevel}, - zeroLevelTol => $num_params{zeroLevelTol}, - ); - - # - # Get the proper Parser object for the professor's answer - # using the initialized context - # - my $oldContext = &$Context($context); my $r; - if ($num_params{units}) { - $r = new Parser::Legacy::NumberWithUnits($correctAnswer); - $options{rh_correct_units} = $num_params{units}; - } else { - $r = Value::Formula->new($correctAnswer); - die "The professor's answer can't be a formula" unless $r->isConstant; - $r = $r->eval; $r = new Value::Real($r) unless Value::class($r) eq 'String'; - $r->{correct_ans} = $correctAnswer; - if ($mode eq 'phase_pi') { - my $pi = 4*atan2(1,1); - while ($r > $pi/2) {$r -= $pi} - while ($r < -$pi/2) {$r += $pi} - } - } - # - # Get the answer checker from the parser object - # - my $cmp = $r->cmp(%options); - $cmp->install_pre_filter(sub { - my $rh_ans = shift; - $rh_ans->{original_student_ans} = $rh_ans->{student_ans}; - $rh_ans->{original_correct_ans} = $rh_ans->{correct_ans}; - return $rh_ans; - }); - $cmp->install_post_filter(sub { - my $rh_ans = shift; - $rh_ans->{student_ans} = $rh_ans->{student_value}->string - if ref($rh_ans->{student_value}); - return $rh_ans; - }); - $cmp->{debug} = $num_params{debug}; - &$Context($oldContext); - - return $cmp; -} - -# -# The original version, for backward compatibility -# (can be removed when the Parser-based version is more fully tested.) -# -sub ORIGINAL_NUM_CMP { # low level numeric compare - my %num_params = @_; - - my @keys = qw ( correctAnswer tolerance tolType format mode zeroLevel zeroLevelTol debug ); - foreach my $key (@keys) { - warn "$key must be defined in options when calling NUM_CMP" unless defined ($num_params{$key}); - } - - my $correctAnswer = $num_params{'correctAnswer'}; - my $format = $num_params{'format'}; - my $mode = $num_params{'mode'}; - - if( $num_params{tolType} eq 'relative' ) { - $num_params{'tolerance'} = .01*$num_params{'tolerance'}; - } - - my $formattedCorrectAnswer; - my $correct_units; - my $correct_num_answer; - my %correct_units; - my $corrAnswerIsString = 0; - - - if (defined($num_params{units}) && $num_params{units}) { - $correctAnswer = str_filters( $correctAnswer, 'trim_whitespace' ); - # units are in form stuff space units where units contains no spaces. - - ($correct_num_answer, $correct_units) = $correctAnswer =~ /^(.*)\s+([^\s]*)$/; - %correct_units = Units::evaluate_units($correct_units); - if ( defined( $correct_units{'ERROR'} ) ) { - warn ("ERROR: The answer \"$correctAnswer\" in the problem definition cannot be parsed:\n" . - "$correct_units{'ERROR'}\n"); - } - # $formattedCorrectAnswer = spf($correct_num_answer,$num_params{'format'}) . " $correct_units"; - $formattedCorrectAnswer = prfmt($correct_num_answer,$num_params{'format'}) . " $correct_units"; - - } elsif (defined($num_params{strings}) && $num_params{strings}) { - my $legalString = ''; - my @legalStrings = @{$num_params{strings}}; - $correct_num_answer = $correctAnswer; - $formattedCorrectAnswer = $correctAnswer; - foreach $legalString (@legalStrings) { - if ( uc($correctAnswer) eq uc($legalString) ) { - $corrAnswerIsString = 1; - - last; - } - } ## at this point $corrAnswerIsString = 0 iff correct answer is numeric - } else { - $correct_num_answer = $correctAnswer; - $formattedCorrectAnswer = prfmt( $correctAnswer, $num_params{'format'} ); - } - - $correct_num_answer = math_constants($correct_num_answer); - - my $PGanswerMessage = ''; - - my ($inVal,$correctVal,$PG_eval_errors,$PG_full_error_report); - - if (defined($correct_num_answer) && $correct_num_answer =~ /\S/ && $corrAnswerIsString == 0 ) { - ($correctVal, $PG_eval_errors,$PG_full_error_report) = PG_answer_eval($correct_num_answer); - } else { # case of a string answer - $PG_eval_errors = ' '; - $correctVal = $correctAnswer; - } - - if ( ($PG_eval_errors && $corrAnswerIsString == 0) or ((not is_a_number($correctVal)) && $corrAnswerIsString == 0)) { - ##error message from eval or above - warn "Error in 'correct' answer: $PG_eval_errors<br> - The answer $correctAnswer evaluates to $correctVal, - which cannot be interpreted as a number. "; - - } - ######################################################################### - - #construct the answer evaluator - my $answer_evaluator = new AnswerEvaluator; - $answer_evaluator->{debug} = $num_params{debug}; - $answer_evaluator->ans_hash( - correct_ans => $correctVal, - type => "${mode}_number", - tolerance => $num_params{tolerance}, - tolType => $num_params{tolType}, - units => $correct_units, - original_correct_ans => $formattedCorrectAnswer, - rh_correct_units => \%correct_units, - answerIsString => $corrAnswerIsString, - ); - my ($in, $formattedSubmittedAnswer); - $answer_evaluator->install_pre_filter(sub {my $rh_ans = shift; - $rh_ans->{original_student_ans} = $rh_ans->{student_ans}; $rh_ans;} - ); - - - - if (defined($num_params{units}) && $num_params{units}) { - $answer_evaluator->install_pre_filter(\&check_units); - } - if (defined($num_params{strings}) && $num_params{strings}) { - $answer_evaluator->install_pre_filter(\&check_strings, %num_params); - } - - ## FIXME? - this pre filter was moved before check_units to allow - ## for latex preview of answers with no units. - ## seems to work but may have unintended side effects elsewhere. - - ## Actually it caused trouble with the check strings package so it has been moved back - # We'll try some other method -- perhaps add code to fix_answer for display - $answer_evaluator->install_pre_filter(\&check_syntax); - - $answer_evaluator->install_pre_filter(\&math_constants); - - if ($mode eq 'std') { - # do nothing - } elsif ($mode eq 'strict') { - $answer_evaluator->install_pre_filter(\&is_a_number); - } elsif ($mode eq 'arith') { - $answer_evaluator->install_pre_filter(\&is_an_arithmetic_expression); - } elsif ($mode eq 'frac') { - $answer_evaluator->install_pre_filter(\&is_a_fraction); - - } elsif ($mode eq 'phase_pi') { - $answer_evaluator->install_pre_filter(\&phase_pi); - - } else { - $PGanswerMessage = 'Tell your professor that there is an error in his or her answer mechanism. No mode was specified.'; - $formattedSubmittedAnswer = $in; - } - - if ($corrAnswerIsString == 0 ){ # avoiding running compare_numbers when correct answer is a string. - $answer_evaluator->install_evaluator(\&compare_numbers, %num_params); - } - - -############################################################################### -# We'll leave these next lines out for now, so that the evaluated versions of the student's and professor's -# can be displayed in the answer message. This may still cause a few anomolies when strings are used -# -############################################################################### - - $answer_evaluator->install_post_filter(\&fix_answers_for_display); - - $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; - return $rh_ans unless $rh_ans->catch_error('EVAL'); - $rh_ans->{student_ans} = $rh_ans->{original_student_ans}. ' '. $rh_ans->{error_message}; - $rh_ans->clear_error('EVAL'); } ); - $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('SYNTAX'); } ); - $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('UNITS'); } ); - $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('NUMBER'); } ); - $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('STRING'); } ); - $answer_evaluator; -} - - - -########################################################################## -########################################################################## -## Function answer evaluators - -=head2 Function Answer Evaluators - -Function answer evaluators take in a function, compare it numerically to a -correct function, and return a score. They can require an exactly equivalent -function, or one that is equal up to a constant. They can accept or reject an -answer based on specified tolerances for numerical deviation. - -Function Comparison Options - - correctEqn -- The correct equation, specified as a string. It may include - all basic arithmetic operations, as well as elementary - functions. Variable usage is described below. - - Variables -- The independent variable(s). When comparing the correct - equation to the student equation, each variable will be - replaced by a certain number of numerical values. If - the student equation agrees numerically with the correct - equation, they are considered equal. Note that all - comparison is numeric; it is possible (although highly - unlikely and never a practical concern) for two unequal - functions to yield the same numerical results. - - Limits -- The limits of evaluation for the independent variables. - Each variable is evaluated only in the half-open interval - [lower_limit, upper_limit). This is useful if the function - has a singularity or is not defined in a certain range. - For example, the function "sqrt(-1-x)" could be evaluated - in [-2,-1). - - Tolerance -- Tolerance in function comparisons works exactly as in - numerical comparisons; see the numerical comparison - documentation for a complete description. Note that the - tolerance does applies to the function as a whole, not - each point individually. - - Number of -- Specifies how many points to evaluate each variable at. This - Points is typically 3, but can be set higher if it is felt that - there is a strong possibility of "false positives." - - Maximum -- Sets the maximum size of the constant of integration. For - Constant of technical reasons concerning floating point arithmetic, if - Integration the additive constant, i.e., the constant of integration, is - greater (in absolute value) than maxConstantOfIntegration - AND is greater than maxConstantOfIntegration times the - correct value, WeBWorK will give an error message saying - that it can not handle such a large constant of integration. - This is to prevent e.g. cos(x) + 1E20 or even 1E20 as being - accepted as a correct antiderivatives of sin(x) since - floating point arithmetic cannot tell the difference - between cos(x) + 1E20, 1E20, and -cos(x) + 1E20. - -Technical note: if you examine the code for the function routines, you will see -that most subroutines are simply doing some basic error-checking and then -passing the parameters on to the low-level FUNCTION_CMP(). Because this routine -is set up to handle multivariable functions, with single-variable functions as -a special case, it is possible to pass multivariable parameters to single- -variable functions. This usage is strongly discouraged as unnecessarily -confusing. Avoid it. - -Default Values (As of 7/24/2000) (Option -- Variable Name -- Value) - - Variable -- $functVarDefault -- 'x' - Relative Tolerance -- $functRelPercentTolDefault -- .1 - Absolute Tolerance -- $functAbsTolDefault -- .001 - Lower Limit -- $functLLimitDefault -- .0000001 - Upper Limit -- $functULimitDefault -- 1 - Number of Points -- $functNumOfPoints -- 3 - Zero Level -- $functZeroLevelDefault -- 1E-14 - Zero Level Tolerance -- $functZeroLevelTolDefault -- 1E-12 - Maximum Constant -- $functMaxConstantOfIntegration -- 1E8 - of Integration - -=cut - - - -=head3 fun_cmp() - -Compares a function or a list of functions, using a named hash of options to set -parameters. This can make for more readable code than using the function_cmp() -style, but some people find one or the other easier to remember. - -ANS( fun_cmp( answer or answer_array_ref, options_hash ) ); - - 1. a string containing the correct function, or a reference to an - array of correct functions - 2. a hash containing the following items (all optional): - var -- either the number of variables or a reference to an - array of variable names (see below) - limits -- reference to an array of arrays of limits (see below), or: - mode -- 'std' (default) (function must match exactly), or: - 'antider' (function must match up to a constant) - relTol -- (default) a relative tolerance (as a percentage), or: - tol -- an absolute tolerance for error - numPoints -- the number of points to evaluate the function at - maxConstantOfIntegration -- maximum size of the constant of integration - zeroLevel -- if the correct answer is this close to zero, then - zeroLevelTol applies - zeroLevelTol -- absolute tolerance to allow when answer is close to zero - test_points -- a list of points to use in checking the function, or a list of lists when there is more than one variable. - params an array of "free" parameters which can be used to adapt - the correct answer to the submitted answer. (e.g. ['c'] for - a constant of integration in the answer x^3/3 + c. - debug -- when set to 1 this provides extra information while checking the - the answer. - - Returns an answer evaluator, or (if given a reference to an array - of answers), a list of answer evaluators - -ANSWER: - - The answer must be in the form of a string. The answer can contain - functions, pi, e, and arithmetic operations. However, the correct answer - string follows a slightly stricter syntax than student answers; specifically, - there is no implicit multiplication. So the correct answer must be "3*x" rather - than "3 x". Students can still enter "3 x". - -VARIABLES: - - The var parameter can contain either a number or a reference to an array of - variable names. If it contains a number, the variables are named automatically - as follows: 1 variable -- x - 2 variables -- x, y - 3 variables -- x, y, z - 4 or more -- x_1, x_2, x_3, etc. - If the var parameter contains a reference to an array of variable names, then - the number of variables is determined by the number of items in the array. A - reference to an array is created with brackets, e.g. "var => ['r', 's', 't']". - If only one variable is being used, you can write either "var => ['t']" for - consistency or "var => 't'" as a shortcut. The default is one variable, x. - -LIMITS: - - Limits are specified with the limits parameter. You may NOT use llimit/ulimit. - If you specify limits for one variable, you must specify them for all variables. - The limit parameter must be a reference to an array of arrays of the form - [lower_limit. upper_limit], each array corresponding to the lower and upper - endpoints of the (half-open) domain of one variable. For example, - "vars => 2, limits => [[0,2], [-3,8]]" would cause x to be evaluated in [0,2) and - y to be evaluated in [-3,8). If only one variable is being used, you can write - either "limits => [[0,3]]" for consistency or "limits => [0,3]" as a shortcut. - -TEST POINTS: - - In some cases, the problem writer may want to specify the points - used to check a particular function. For example, if you want to - use only integer values, they can be specified. With one variable, - you can specify "test_points => [1,4,5,6]" or "test_points => [[1,4,5,6]]". - With more variables, specify the list for the first variable, then the - second, and so on: "vars=>['x','y'], test_points => [[1,4,5],[7,14,29]]". - - If the problem writer wants random values which need to meet some special - restrictions (such as being integers), they can be generated in the problem: - "test_points=>[random(1,50), random(1,50), random(1,50), random(1,50)]". - - Note that test_points should not be used for function checks which involve - parameters (either explicitly given by "params", or as antiderivatives). - -EXAMPLES: - - fun_cmp( "3*x" ) -- standard compare, variable is x - fun_cmp( ["3*x", "4*x+3", "3*x**2"] )... [truncated message content] |