Forum archive 2000-2006

Michael Gage - PGcomplexmacros.pl

Michael Gage - PGcomplexmacros.pl

by Arnold Pizer -
Number of replies: 0
inactiveTopicPGcomplexmacros.pl topic started 5/15/2002; 7:53:07 PM
last post 5/15/2002; 7:53:07 PM
userMichael Gage - PGcomplexmacros.pl  blueArrow
5/15/2002; 7:53:07 PM (reads: 1833, responses: 0)


NAME

    Macros for complex numbers for the PG language


SYNPOSIS


DESCRIPTION

cplx_cmp

    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.

multi_cmp

    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.

single_term() This subroutine takes in a string, which is a mathematical expresion, and determines whether or not it is a single term. This is accoplished using a stack. Open parenthesis pluses and minuses are all added onto the stack, and when a closed parenthesis is reached, the stack is popped untill the open parenthesis is found. If the original was a single term, the stack should be empty after evaluation. If there is anything left ( + or - ) then false is returned. Of course, the unary operator "-" must be handled... if it is a unary operator, and not a regular - the only place it could occur unambiguously without being surrounded by parenthesis, is the very first position. So that case is checked before the loop begins. =cut

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 |>