Macros for complex numbers for the PG language
This subroutine compares complex numbers. Available prefilters include: each of these are called by cplx_cmp( answer, mode => '(prefilter name)' ) 'std' The standard comparison method for complex numbers. This option it the default and works with any combination of cartesian numbers, polar numbers, and functions. The default display method is cartesian, for all methods, but if the student answer is polar, even in part, then their answer will be displayed that way. 'strict_polar' This is still under developement. The idea is to check to make sure that there only a single term in front of the e and after it... but the method does not check to make sure that the i is in the exponent, nor does it handle cases where the polar has e** coefficients. 'strict_num_cartesian' This prefilter allows only complex numbers of the form "a+bi" where a and b are strictly numbers. 'strict_num_polar' This prefilter allows only complex numbers of the form "ae^(bi)" where a and b are strictly numbers. 'strict' This is a combination of strict_num_cartesian and strict_num_polar, so it allows complex numbers of either the form "a+bi" or "ae^(bi)" where a and b are strictly numbers.
Checks a comma separated string of items against an array of evaluators. For example this is useful for checking all of the complex roots of an equation. Each student answer must be evaluated as correct by a DISTINCT answer evalutor.
This answer checker will only work reliably if each answer checker corresponds to a distinct correct answer. For example if one answer checker requires any positive number, and the second requires the answer 1, then 1,2 might be judged incorrect since 1, satisifes the first answer checker, but 2 doesn't satisfy the second. 2,1 would work however. Avoid this type of use!!
Including backtracking to fit the answers as best possible to each answer evaluator in the best possible way, is beyond the ambitions of this evaluator.
sub single_term{
my $term = shift;
my @stack;
$term = reverse $term;
if( length $term >= 1 )
{
my $temp = chop $term;
if( $temp ne "-" ){ $term .= $temp; }
}
while( length $term >= 1 ){
my $character = chop $term;
if( $character eq "+" || $character eq "-" || $character eq "(" ){
push @stack, $character;
}elsif( $character eq ")" ){
while( pop @stack ne "(" ){}
}
}
if( scalar @stack == 0 ){ return 1;}else{ return 0;}
}
# changes default to display as a polar
sub fix_for_polar_display{
my ($rh_ans, %options) = @_;
if( ref( $rh_ans->{student_ans} ) =~ /Complex/ && $rh_ans->{answer_form} eq 'polar'
){
$rh_ans->{student_ans}->display_format( 'polar');
## these lines of code have the polar displayed as re^(theta) instead of
[rho,theta]
$rh_ans->{student_ans} =~ s/,/*e^\(/;
$rh_ans->{student_ans} =~ s/\[//;
$rh_ans->{student_ans} =~ s/\]/i\)/;
}
$rh_ans;
}
# this does not seem to be in use, so I'm commenting it out. Mike Gage 6/27/05
# sub cplx_cmp2 {
# my $correctAnswer = shift;
# my %cplx_params = @_;
# my @keys = qw ( correctAnswer tolerance tolType format mode zeroLevel zeroLevelTol
debug );
# assign_option_aliases( \%cplx_params,
# 'reltol' => 'relTol',
# );
# set_default_options(\%cplx_params,
# 'tolType' => (defined($cplx_params{tol}) ) ? 'absolute'
: 'relative',
# # default mode should be relative, to obtain this tol must
not be defined
# 'tolerance' => $main::numAbsTolDefault,
# 'relTol' => $main::numRelPercentTolDefault,
# 'zeroLevel' => $main::numZeroLevelDefault,
# 'zeroLevelTol' => $main::numZeroLevelTolDefault,
# 'format' => $main::numFormatDefault,
# 'debug' => 0,
# 'mode' => 'std',
#
# );
# $correctAnswer = cplx($correctAnswer,0) unless ref($correctAnswer) =~/Complex/;
# my $format = $cplx_params{'format'};
# my $mode = $cplx_params{'mode'};
#
# if( $cplx_params{tolType} eq 'relative' ) {
# $cplx_params{'tolerance'} = .01*$cplx_params{'relTol'};
# }
#
# my $formattedCorrectAnswer;
# my $correct_num_answer;
# my $corrAnswerIsString = 0;
#
#
# if (defined($cplx_params{strings}) && $cplx_params{strings}) {
# my $legalString = ";
# my @legalStrings = @{$cplx_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, $cplx_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. ";
#
# }
# ########################################################################
# $correctVal = $correct_num_answer;#it took me two and a half hours to figure
out that correctVal wasn't
# #getting the number properly
# #construct the answer evaluator
# my $answer_evaluator = new AnswerEvaluator;
#
#
# $answer_evaluator->{debug} = $cplx_params{debug};
# $answer_evaluator->ans_hash(
# correct_ans => $correctVal,
# type => "${mode}_number",
# tolerance => $cplx_params{tolerance},
# tolType => 'absolute', # $cplx_params{tolType},
# original_correct_ans => $formattedCorrectAnswer,
# answerIsString => $corrAnswerIsString,
# answer_form => 'cartesian',
# );
# 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($cplx_params{strings}) && $cplx_params{strings}) {
# $answer_evaluator->install_pre_filter(\&check_strings, %cplx_params);
# }
# #$answer_evaluator->install_pre_filter(\&check_syntax);
#
# $answer_evaluator->install_pre_filter(\&math_constants);
# $answer_evaluator->install_pre_filter(\&cplx_constants);
# $answer_evaluator->install_pre_filter(\&check_for_polar);
# if ($mode eq 'std') {
# # do nothing
# } elsif ($mode eq 'strict_polar') {
# $answer_evaluator->install_pre_filter(\&is_a_polar);
# } elsif ($mode eq 'strict_num_cartesian') {
# $answer_evaluator->install_pre_filter(\&is_a_numeric_cartesian);
# } elsif ($mode eq 'strict_num_polar') {
# $answer_evaluator->install_pre_filter(\&is_a_numeric_polar);
# } elsif ($mode eq 'strict') {
# $answer_evaluator->install_pre_filter(\&is_a_numeric_complex);
# } 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);
#
# } 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_cplx2, %cplx_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(\&fix_for_polar_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('POLAR');
} );
# $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('CARTESIAN');
} );
# $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('COMPLEX');
} );
# $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('STRING');
} );
# $answer_evaluator;
# }
# compares two complex numbers by comparing their real and imaginary parts
# this does not seem to be in use, so I'm commenting it out. Mike Gage 6/27/05
# sub compare_cplx2 {
# my ($rh_ans, %options) = @_;
# my @answers = split/,/,$rh_ans->{student_ans};
# foreach( @answers )
# {
# $rh_ans->{student_ans} = $_;
# $rh_ans = &check_syntax( $rh_ans );
# my ($inVal,$PG_eval_errors,$PG_full_error_report) = PG_answer_eval($rh_ans->{student_ans});
#
# if ($PG_eval_errors) {
# $rh_ans->throw_error('EVAL','There is a syntax error in your answer');
# $rh_ans->{ans_message} = clean_up_error_msg($PG_eval_errors);
# # return $rh_ans;
# } else {
# $rh_ans->{student_ans} = prfmt($inVal,$options{format});
# }
#
# $inVal = cplx($inVal,0) unless ref($inVal) =~/Complex/;
# my $permitted_error_Re;
# my $permitted_error_Im;
# if ($rh_ans->{tolType} eq 'absolute') {
# $permitted_error_Re = $rh_ans->{tolerance};
# $permitted_error_Im = $rh_ans->{tolerance};
# }
# elsif ( abs($rh_ans->{correct_ans}) <= $options{zeroLevel}) {
# $permitted_error_Re = $options{zeroLevelTol}; ## want $tol to be non
zero
# $permitted_error_Im = $options{zeroLevelTol}; ## want $tol to be non
zero
# }
# else {
# $permitted_error_Re = abs($rh_ans->{tolerance}*$rh_ans->{correct_ans}->Complex::Re);
# $permitted_error_Im = abs($rh_ans->{tolerance}*$rh_ans->{correct_ans}->Complex::Im);
#
# }
#
# $rh_ans->{score} = 1 if ( abs( $rh_ans->{correct_ans}->Complex::Re - $inVal->Complex::Re)
<=
# $permitted_error_Re && abs($rh_ans->{correct_ans}->Complex::Im - $inVal->Complex::Im
)<= $permitted_error_Im );
# if( $rh_ans->{score} == 1 ){ return $rh_ans; }
#
#
# }
# $rh_ans;
#
# }
# this does not seem to be in use, so I'm commenting it out. Mike Gage 6/27/05
# sub cplx_cmp_mult {
# my $correctAnswer = shift;
# my %cplx_params = @_;
# my @keys = qw ( correctAnswer tolerance tolType format mode zeroLevel zeroLevelTol
debug );
# assign_option_aliases( \%cplx_params,
# 'reltol' => 'relTol',
# );
# set_default_options(\%cplx_params,
# 'tolType' => (defined($cplx_params{tol}) ) ? 'absolute' :
'relative',
# # default mode should be relative, to obtain this tol must not
be defined
# 'tolerance' => $main::numAbsTolDefault,
# 'relTol' => $main::numRelPercentTolDefault,
# 'zeroLevel' => $main::numZeroLevelDefault,
# 'zeroLevelTol' => $main::numZeroLevelTolDefault,
# 'format' => $main::numFormatDefault,
# 'debug' => 0,
# 'mode' => 'std',
#
# );
# $correctAnswer = cplx($correctAnswer,0) unless ref($correctAnswer) =~/Complex/;
# my $format = $cplx_params{'format'};
# my $mode = $cplx_params{'mode'};
#
# if( $cplx_params{tolType} eq 'relative' ) {
# $cplx_params{'tolerance'} = .01*$cplx_params{'relTol'};
# }
#
# my $formattedCorrectAnswer;
# my $correct_num_answer;
# my $corrAnswerIsString = 0;
#
#
# if (defined($cplx_params{strings}) && $cplx_params{strings}) {
# my $legalString = ";
# my @legalStrings = @{$cplx_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, $cplx_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. ";
#
# }
# ########################################################################
# $correctVal = $correct_num_answer;#it took me two and a half hours to figure
out that correctVal wasn't
# #getting the number properly
# #construct the answer evaluator
# my $counter = 0;
# my $answer_evaluator = new AnswerEvaluator;
#
# my $number;
# $answer_evaluator->install_pre_filter( sub{ my $rh_ans = shift; my @temp =
# split/,/,$rh_ans->{student_ans}; $number = @temp; warn "this number ", $number;
$rh_ans;});
# warn "number ", $number;
# while( $counter < 4 )
# {
# $answer_evaluator = &answer_mult( $correctVal, $mode, $formattedCorrectAnswer,
# $corrAnswerIsString, $counter, %cplx_params );
# warn "answer_evaluator ", $answer_evaluator;
# $answer_evaluator->install_evaluator( sub { my $rh_ans = shift; warn "score ",
$rh_ans->{score};
# $rh_ans;});
# $counter += 1;
# }
#
# $answer_evaluator;
#
# }
# this does not seem to be in use, so I'm commenting it out. Mike Gage 6/27/05
# sub answer_mult{
# my $correctVal = shift;
# my $mode = shift;
# my $formattedCorrectAnswer = shift;
# my $corrAnswerIsString = shift;
# my $counter = shift;
# warn "counter ", $counter;
#
# my %cplx_params = @_;
# my $answer_evaluator = new AnswerEvaluator;
#
#
# $answer_evaluator->{debug} = $cplx_params{debug};
# $answer_evaluator->ans_hash(
# correct_ans => $correctVal,
# type => "${mode}_number",
# tolerance => $cplx_params{tolerance},
# tolType => 'absolute', # $cplx_params{tolType},
# original_correct_ans => $formattedCorrectAnswer,
# answerIsString => $corrAnswerIsString,
# answer_form => 'cartesian',
# );
# $answer_evaluator->install_pre_filter(sub {
# my $rh_ans = shift;
# $rh_ans->{original_student_ans} = $rh_ans->{student_ans};
# my @answers = split/,/,$rh_ans->{student_ans};
# $rh_ans -> {student_ans} = $answers[$counter];
# $rh_ans;
# }
# );
# if (defined($cplx_params{strings}) && $cplx_params{strings}) {
# $answer_evaluator->install_pre_filter(\&check_strings, %cplx_params);
# }
# $answer_evaluator->install_pre_filter(\&check_syntax);
# $answer_evaluator->install_pre_filter(\&math_constants);
# $answer_evaluator->install_pre_filter(\&cplx_constants);
# $answer_evaluator->install_pre_filter(\&check_for_polar);
# if ($mode eq 'std') {
# # do nothing
# } elsif ($mode eq 'strict_polar') {
# $answer_evaluator->install_pre_filter(\&is_a_polar);
# } elsif ($mode eq 'strict_num_cartesian') {
# $answer_evaluator->install_pre_filter(\&is_a_numeric_cartesian);
# } elsif ($mode eq 'strict_num_polar') {
# $answer_evaluator->install_pre_filter(\&is_a_numeric_polar);
# } elsif ($mode eq 'strict') {
# $answer_evaluator->install_pre_filter(\&is_a_numeric_complex);
# } 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);
#
# } else {
# #$PGanswerMessage = 'Tell your professor that there is an error in
his or her answer mechanism. No mode was specified.';
# }
# if ($corrAnswerIsString == 0 ){ # avoiding running compare_numbers when correct
answer is a string.
# $answer_evaluator->install_evaluator(\&compare_cplx, %cplx_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(\&fix_for_polar_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('POLAR');
} );
# $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('CARTESIAN');
} );
# $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('COMPLEX');
} );
# $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; warn "ans hash",
$rh_ans->clear_error('STRING'); } );
# $answer_evaluator;
# }
#
1;
File path = /ww/webwork/pg/macros/PGcomplexmacros.pl
<| Post or View Comments |> |