num_cmp() 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
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_errorsstr_cmp([\$ans1, \$ans2],%options)
# or num_cmp([\$num1, \$num2], %options)[\$ans1, \$ans2]";
# }
#}
# simple subroutine to display an error message when
# function compares are called with invalid parameters
sub function_invalid_params {
my $correctEqn = shift @_;
my $error_response = sub {
my $PGanswerMessage = "Tell your professor that there is an error with the parameters " .
"to the function answer evaluator";
return ( 0, $correctEqn, "", $PGanswerMessage );
};
return $error_response;
}
sub clean_up_error_msg {
my $msg = $_[0];
$msg =~ s/^\[[^\]]*\][^:]*://;
$msg =~ s/Unquoted string//g;
$msg =~ s/may\s+clash.*/does not make sense here/;
$msg =~ s/\sat.*line [\d]*//g;
$msg = 'error: '. $msg;
return $msg;
}
#formats the student and correct answer as specified
#format must be of a form suitable for sprintf (e.g. '%0.5g'),
#with the exception that a '#' at the end of the string
#will cause trailing zeros in the decimal part to be removed
sub prfmt {
my($number,$format) = @_; # attention, the order of format and number are reversed
my $out;
if ($format) {
warn "Incorrect format used: $format. | $key | => | ".pretty_print($r_input->{$key}) . " |