[system] / trunk / webwork / system / courseScripts / PGcomplexmacros.pl Repository:
ViewVC logotype

View of /trunk/webwork/system/courseScripts/PGcomplexmacros.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 387 - (download) (as text) (annotate)
Wed Jun 19 13:54:23 2002 UTC (17 years, 3 months ago) by lr003k
File size: 48379 byte(s)
added a multiple method, still a little buggy, but it currently works, I still need to document it as well

    1 #!/usr/local/bin/perl
    2 # This file     is PGcomplexmacros2.pl
    3 # This includes the subroutines for the ANS macros, that
    4 # is, macros allowing a more flexible answer checking
    5 ####################################################################
    6 # Copyright @ 1995-2002 The WeBWorK Team
    7 # All Rights Reserved
    8 ####################################################################
    9 #$Id$
   10 
   11 
   12 =head1 NAME
   13 
   14   Macros for complex numbers for the PG language
   15 
   16 =head1 SYNPOSIS
   17 
   18 
   19 
   20 =head1 DESCRIPTION
   21 
   22 =cut
   23 
   24 
   25 BEGIN{
   26   be_strict();
   27 
   28 }
   29 
   30 
   31 
   32 sub _PGcomplexmacros_init {
   33 }
   34 # export functions from Complex1.
   35 
   36 foreach my $f (@Complex1::EXPORT) {
   37     #PG_restricted_eval("\*$f = \*Complex1::$f"); # this is too clever --
   38                                                   # the original subroutines are destroyed
   39         next if $f eq 'sqrt';  #exporting the square root caused conflicts with the standard version
   40                                # You can still use Complex1::sqrt to take square root of complex numbers
   41         next if $f eq 'log';  #exporting loq caused conflicts with the standard version
   42                                # You can still use Complex1::log to take square root of complex numbers
   43 
   44     my $string = qq{
   45        sub main::$f {
   46            &Complex1::$f;
   47        }
   48     };
   49     PG_restricted_eval($string);
   50 }
   51 
   52 # You need to add
   53 # sub i();  # to your problem or else to dangerousMacros.pl
   54 # in order to use expressions such as 1 +3*i;
   55 # Without this prototype you would have to write 1+3*i();
   56 # The prototype has to be defined at compile time, but dangerousMacros.pl is complied first.
   57 #Complex1::display_format('cartesian');
   58 
   59 # number format used frequently in strict prefilters
   60 my $number = '([+-]?)(?=\d|\.\d)\d*(\.\d*)?(E([+-]?\d+))?';
   61 
   62 
   63 
   64 sub polar{
   65   my $z = shift;
   66   my %options = @_;
   67     my $r = rho($z);
   68     my $theta = $z->theta;
   69     my $r_format = ':%0.3f';
   70     my $theta_format = ':%0.3f';
   71     $r_format=":" . $options{r_format} if defined($options{r_format});
   72     $theta_format = ":" . $options{theta_format} if defined($options{theta_format});
   73     "{$r$r_format} e^{i {$theta$theta_format}}";
   74 
   75 }
   76 
   77 
   78 
   79 sub cplx_cmp {
   80   my $correctAnswer = shift;
   81   my %cplx_params = @_;
   82   my @keys = qw ( correctAnswer tolerance tolType format mode zeroLevel zeroLevelTol debug );
   83   assign_option_aliases( \%cplx_params,
   84                 'reltol'    =>      'relTol',
   85       );
   86       set_default_options(\%cplx_params,
   87               'tolType'   =>  (defined($cplx_params{tol}) ) ? 'absolute' : 'relative',
   88                  # default mode should be relative, to obtain this tol must not be defined
   89               'tolerance'     =>  $main::numAbsTolDefault,
   90                     'relTol'    =>  $main::numRelPercentTolDefault,
   91             'zeroLevel'   =>  $main::numZeroLevelDefault,
   92             'zeroLevelTol'  =>  $main::numZeroLevelTolDefault,
   93             'format'    =>  $main::numFormatDefault,
   94             'debug'     =>  0,
   95             'mode'      => 'std',
   96 
   97       );
   98   $correctAnswer = cplx($correctAnswer,0) unless ref($correctAnswer) =~/Complex/;
   99   my $format    = $cplx_params{'format'};
  100   my $mode    = $cplx_params{'mode'};
  101 
  102   if( $cplx_params{tolType} eq 'relative' ) {
  103     $cplx_params{'tolerance'} = .01*$cplx_params{'tolerance'};
  104   }
  105 
  106   my $formattedCorrectAnswer;
  107   my $correct_num_answer;
  108   my $corrAnswerIsString = 0;
  109 
  110 
  111   if (defined($cplx_params{strings}) && $cplx_params{strings}) {
  112     my $legalString = '';
  113     my @legalStrings = @{$cplx_params{strings}};
  114     $correct_num_answer = $correctAnswer;
  115     $formattedCorrectAnswer = $correctAnswer;
  116     foreach $legalString (@legalStrings) {
  117       if ( uc($correctAnswer) eq uc($legalString) ) {
  118         $corrAnswerIsString = 1;
  119 
  120         last;
  121       }
  122     }     ## at this point $corrAnswerIsString = 0 iff correct answer is numeric
  123   } else {
  124     $correct_num_answer = $correctAnswer;
  125     $formattedCorrectAnswer = prfmt( $correctAnswer, $cplx_params{'format'} );
  126   }
  127   $correct_num_answer = math_constants($correct_num_answer);
  128   my $PGanswerMessage = '';
  129 
  130   my ($inVal,$correctVal,$PG_eval_errors,$PG_full_error_report);
  131 
  132   if (defined($correct_num_answer) && $correct_num_answer =~ /\S/ && $corrAnswerIsString == 0 ) {
  133       ($correctVal, $PG_eval_errors,$PG_full_error_report) = PG_answer_eval($correct_num_answer);
  134   } else { # case of a string answer
  135     $PG_eval_errors = ' ';
  136     $correctVal = $correctAnswer;
  137   }
  138   ## This throws an error all the time, and I don't know what it's for
  139   #if ( ($PG_eval_errors && $corrAnswerIsString == 0) or ((not is_a_number($correctVal)) && $corrAnswerIsString == 0)) {
  140         ##error message from eval or above
  141     #warn "Error in 'correct' answer: $PG_eval_errors<br>
  142      #     The answer $correctAnswer evaluates to $correctVal,
  143       #    which cannot be interpreted as a number.  ";
  144 
  145   #}
  146   ########################################################################
  147   $correctVal = $correct_num_answer;#it took me two and a half hours to figure out that correctVal wasn't
  148   #getting the number properly
  149   #construct the answer evaluator
  150       my $answer_evaluator = new AnswerEvaluator;
  151 
  152 
  153       $answer_evaluator->{debug} = $cplx_params{debug};
  154       $answer_evaluator->ans_hash(
  155                 correct_ans       =>  $correctVal,
  156                 type        =>  "${mode}_number",
  157                 tolerance     =>  $cplx_params{tolerance},
  158             tolType       =>  'absolute', # $cplx_params{tolType},
  159             original_correct_ans    =>  $formattedCorrectAnswer,
  160                 answerIsString      =>  $corrAnswerIsString,
  161             answer_form     =>  'cartesian',
  162       );
  163       my ($in, $formattedSubmittedAnswer);
  164   $answer_evaluator->install_pre_filter(sub {my $rh_ans = shift;
  165     $rh_ans->{original_student_ans} = $rh_ans->{student_ans}; $rh_ans;}
  166   );
  167   if (defined($cplx_params{strings}) && $cplx_params{strings}) {
  168       $answer_evaluator->install_pre_filter(\&check_strings, %cplx_params);
  169   }
  170 
  171   $answer_evaluator->install_pre_filter(\&check_syntax);
  172 
  173   $answer_evaluator->install_pre_filter(\&math_constants);
  174   $answer_evaluator->install_pre_filter(\&cplx_constants);
  175   $answer_evaluator->install_pre_filter(\&check_for_polar);
  176   if ($mode eq 'std') {
  177         # do nothing
  178   } elsif ($mode eq 'strict_polar') {
  179     $answer_evaluator->install_pre_filter(\&is_a_polar);
  180   } elsif ($mode eq 'strict_num_cartesian') {
  181     $answer_evaluator->install_pre_filter(\&is_a_numeric_cartesian);
  182   } elsif ($mode eq 'strict_num_polar') {
  183     $answer_evaluator->install_pre_filter(\&is_a_numeric_polar);
  184   } elsif ($mode eq 'strict') {
  185     $answer_evaluator->install_pre_filter(\&is_a_numeric_complex);
  186   } elsif ($mode eq 'arith') {
  187       $answer_evaluator->install_pre_filter(\&is_an_arithmetic_expression);
  188     } elsif ($mode eq 'frac') {
  189       $answer_evaluator->install_pre_filter(\&is_a_fraction);
  190 
  191     } else {
  192       $PGanswerMessage = 'Tell your professor that there is an error in his or her answer mechanism. No mode was specified.';
  193       $formattedSubmittedAnswer = $in;
  194     }
  195   if ($corrAnswerIsString == 0 ){   # avoiding running compare_numbers when correct answer is a string.
  196     $answer_evaluator->install_evaluator(\&compare_cplx, %cplx_params);
  197   }
  198 
  199 
  200 ###############################################################################
  201 # We'll leave these next lines out for now, so that the evaluated versions of the student's and professor's
  202 # can be displayed in the answer message.  This may still cause a few anomolies when strings are used
  203 #
  204 ###############################################################################
  205 
  206   $answer_evaluator->install_post_filter(\&fix_answers_for_display);
  207   $answer_evaluator->install_post_filter(\&fix_for_polar_display);
  208 
  209       $answer_evaluator->install_post_filter(sub {my $rh_ans = shift;
  210           return $rh_ans unless $rh_ans->catch_error('EVAL');
  211           $rh_ans->{student_ans} = $rh_ans->{original_student_ans}. ' '. $rh_ans->{error_message};
  212           $rh_ans->clear_error('EVAL'); } );
  213       $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('SYNTAX'); } );
  214       $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('POLAR'); } );
  215       $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('CARTESIAN'); } );
  216       $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('COMPLEX'); } );
  217   $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('STRING'); } );
  218       $answer_evaluator;
  219 }
  220 
  221 
  222 
  223 
  224 
  225 sub cplx_cmp4{
  226   my $number_of_answers = shift;
  227   my $count = 0; my @answers;
  228   while( $count < $number_of_answers )
  229   {
  230     $answers[$count] = shift;
  231     $count++;
  232   }
  233   my %cplx_params = @_;
  234   my @keys = qw ( correctAnswer tolerance tolType format mode zeroLevel zeroLevelTol debug );
  235   my @correctVal;
  236   my $formattedCorrectAnswer;
  237   my @correct_num_answer;
  238   my ($PG_eval_errors,$PG_full_error_report);
  239   assign_option_aliases( \%cplx_params,
  240                 'reltol'    =>      'relTol',
  241       );
  242       set_default_options(\%cplx_params,
  243               'tolType'   =>  (defined($cplx_params{tol}) ) ? 'absolute' : 'relative',
  244               # default mode should be relative, to obtain this tol must not be defined
  245           'tolerance'   =>  $main::numAbsTolDefault,
  246                       'relTol'    =>  $main::numRelPercentTolDefault,
  247           'zeroLevel'   =>  $main::numZeroLevelDefault,
  248           'zeroLevelTol'    =>  $main::numZeroLevelTolDefault,
  249           'format'    =>  $main::numFormatDefault,
  250           'debug'     =>    0,
  251           'mode'      =>  'std',
  252 
  253       );
  254   foreach( @answers )
  255   {
  256     $_ = cplx( $_, 0 ) unless ref($_) =~/Complex/;
  257   }
  258   my $format    = $cplx_params{'format'};
  259   my $mode    = $cplx_params{'mode'};
  260 
  261   if( $cplx_params{tolType} eq 'relative' ) {
  262     $cplx_params{'tolerance'} = .01*$cplx_params{'tolerance'};
  263   }
  264   #my $correctAnswer = $answers[0];
  265 
  266   my $corrAnswerIsString = 0;
  267 
  268   for( my $k = 0; $k < $number_of_answers; $k++ ){
  269   if (defined($cplx_params{strings}) && $cplx_params{strings}) {
  270     my $legalString = '';
  271     my @legalStrings = @{$cplx_params{strings}};
  272     $correct_num_answer[$k] = $answers[$k];
  273     $formattedCorrectAnswer .= $answers[$k] . ",";
  274     foreach $legalString (@legalStrings) {
  275       if ( uc($answers[$k]) eq uc($legalString) ) {
  276         $corrAnswerIsString = 1;
  277 
  278         last;
  279       }
  280     }   ## at this point $corrAnswerIsString = 0 iff correct answer is numeric
  281   } else {
  282     $correct_num_answer[$k] = $answers[$k];
  283     $formattedCorrectAnswer .= prfmt( $answers[$k], $cplx_params{'format'} ). ", ";
  284   }
  285   $correct_num_answer[$k] = math_constants($correct_num_answer[$k]);
  286   my $PGanswerMessage = '';
  287 
  288 
  289   if (defined($correct_num_answer[$k]) && $correct_num_answer[$k] =~ /\S/ && $corrAnswerIsString == 0 ) {
  290       ($correctVal[$k], $PG_eval_errors,$PG_full_error_report) =
  291       PG_answer_eval($correct_num_answer[$k]);
  292   } else { # case of a string answer
  293     $PG_eval_errors = ' ';
  294     $correctVal[$k] = $answers[$k];
  295   }
  296 
  297   if ( ($PG_eval_errors && $corrAnswerIsString == 0) or ((not is_a_number($correctVal[$k])) && $corrAnswerIsString == 0)) {
  298         ##error message from eval or above
  299     warn "Error in 'correct' answer: $PG_eval_errors<br>
  300           The answer $answers[$k] evaluates to $correctVal[$k],
  301           which cannot be interpreted as a number.  ";
  302 
  303   }
  304   ########################################################################
  305   $correctVal[$k] = $correct_num_answer[$k];#it took me two and a half hours to figure out that correctVal wasn't
  306   }
  307   #getting the number properly
  308   #construct the answer evaluator
  309 
  310       my $answer_evaluator = new AnswerEvaluator;
  311 
  312 
  313       $answer_evaluator->{debug} = $cplx_params{debug};
  314       $answer_evaluator->ans_hash(
  315                 correct_ans       =>  [@correctVal],
  316                 type        =>  "${mode}_number",
  317                 tolerance     =>  $cplx_params{tolerance},
  318             tolType       =>  'absolute', # $cplx_params{tolType},
  319             original_correct_ans    =>  $formattedCorrectAnswer,
  320                 answerIsString      =>  $corrAnswerIsString,
  321             answer_form     =>  'cartesian',
  322       );
  323       my ($in, $formattedSubmittedAnswer);
  324     $answer_evaluator->install_pre_filter(sub {my $rh_ans = shift;
  325     $rh_ans->{original_student_ans} = $rh_ans->{student_ans}; $rh_ans;}
  326   );
  327   if (defined($cplx_params{strings}) && $cplx_params{strings}) {
  328       $answer_evaluator->install_pre_filter(\&check_strings, %cplx_params);
  329   }
  330   #$answer_evaluator->install_pre_filter(\&check_syntax);
  331 
  332   $answer_evaluator->install_pre_filter(\&math_constants);
  333   $answer_evaluator->install_pre_filter(\&cplx_constants);
  334   $answer_evaluator->install_pre_filter(\&check_for_polar);
  335   if ($mode eq 'std') {
  336         # do nothing
  337   } elsif ($mode eq 'strict_polar') {
  338     $answer_evaluator->install_pre_filter(\&is_a_polar);
  339   } elsif ($mode eq 'strict_num_cartesian') {
  340     $answer_evaluator->install_pre_filter(\&is_a_numeric_cartesian);
  341   } elsif ($mode eq 'strict_num_polar') {
  342     $answer_evaluator->install_pre_filter(\&is_a_numeric_polar);
  343   } elsif ($mode eq 'strict') {
  344     $answer_evaluator->install_pre_filter(\&is_a_numeric_complex);
  345   } elsif ($mode eq 'arith') {
  346       $answer_evaluator->install_pre_filter(\&is_an_arithmetic_expression);
  347     } elsif ($mode eq 'frac') {
  348       $answer_evaluator->install_pre_filter(\&is_a_fraction);
  349 
  350     } else {
  351       #$PGanswerMessage = 'Tell your professor  that there is an error in his or her answer mechanism. No mode was specified.';
  352       $formattedSubmittedAnswer = $in;
  353     }
  354   if ($corrAnswerIsString == 0 ){   # avoiding running compare_numbers when correct answer is a string.
  355     $answer_evaluator->install_evaluator(\&compare_cplx4, %cplx_params);
  356   }
  357 
  358 
  359 ###############################################################################
  360 # We'll leave these next lines out for now, so that the evaluated versions of the student's and professor's
  361 # can be displayed in the answer message.  This may still cause a few anomolies when strings are used
  362 #
  363 ###############################################################################
  364   #$answer_evaluator->install_post_filter( sub{my $rh_ans = shift; $rh_ans->{student_ans} = $rh_ans->{original_student_ans};$rh_ans;});
  365   $answer_evaluator->install_post_filter(\&fix_answers_for_display);
  366   $answer_evaluator->install_post_filter(\&fix_for_polar_display);
  367 
  368       $answer_evaluator->install_post_filter(sub {my $rh_ans = shift;
  369           return $rh_ans unless $rh_ans->catch_error('EVAL');
  370           $rh_ans->{student_ans} = $rh_ans->{original_student_ans}. ' '. $rh_ans->{error_message};
  371           $rh_ans->clear_error('EVAL'); } );
  372       $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('SYNTAX'); } );
  373       $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('POLAR'); } );
  374       $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('CARTESIAN'); } );
  375       $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('COMPLEX'); } );
  376   $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('STRING'); } );
  377       $answer_evaluator;
  378 }
  379 
  380 
  381 # compares two complex numbers by comparing their real and imaginary parts
  382 sub compare_cplx4 {
  383   my ($rh_ans, %options) = @_;
  384   my @student_answers = split/,/,$rh_ans->{student_ans};
  385   my @correct_answers = @{$rh_ans->{correct_ans}};
  386   my $one_correct = 1/@correct_answers;
  387   foreach( @student_answers )
  388   {
  389     $rh_ans->{student_ans} = $_;
  390     $rh_ans = &check_syntax( $rh_ans );
  391     my ($inVal,$PG_eval_errors,$PG_full_error_report) = PG_answer_eval($rh_ans->{student_ans});
  392 
  393     if ($PG_eval_errors) {
  394       $rh_ans->throw_error('EVAL','There is a syntax error in your answer');
  395       $rh_ans->{ans_message} = clean_up_error_msg($PG_eval_errors);
  396       # return $rh_ans;
  397     } else {
  398       $rh_ans->{student_ans} = prfmt($inVal,$options{format});
  399     }
  400 
  401     $inVal = cplx($inVal,0) unless ref($inVal) =~/Complex/;
  402     my $permitted_error_Re;
  403     my $permitted_error_Im;
  404     foreach( @correct_answers ){
  405       if ($rh_ans->{tolType} eq 'absolute') {
  406         $permitted_error_Re = $rh_ans->{tolerance};
  407         $permitted_error_Im = $rh_ans->{tolerance};
  408       }
  409       elsif ( abs($_) <= $options{zeroLevel}) {
  410           $permitted_error_Re = $options{zeroLevelTol};  ## want $tol to be non zero
  411           $permitted_error_Im = $options{zeroLevelTol};  ## want $tol to be non zero
  412       }
  413       else {
  414         $permitted_error_Re =  abs($rh_ans->{tolerance}*$_->Complex::Re);
  415         $permitted_error_Im =  abs($rh_ans->{tolerance}*$_->Complex::Im);
  416 
  417       }
  418 
  419       if ( abs( $_->Complex::Re - $inVal->Complex::Re) <=$permitted_error_Re &&
  420       abs($_->Complex::Im - $inVal->Complex::Im )<= $permitted_error_Im  ){
  421         $rh_ans->{score} += $one_correct ;
  422       }
  423 
  424       if( $rh_ans->{score} == 1 ){ return $rh_ans; }
  425     }
  426 
  427   }
  428   $rh_ans;
  429 
  430 }
  431 
  432 
  433 
  434 sub mult_cmp{
  435   my $number_of_answers = shift;
  436   my @answers;
  437   for( my $count = 0; $count < $number_of_answers; $count++ )
  438   {
  439     $answers[$count] = shift;
  440   }
  441   my %mult_params = @_;
  442   my @keys = qw ( tolerance tolType format mode zeroLevel zeroLevelTol debug );
  443   my @correctVal;
  444   my $formattedCorrectAnswer;
  445   my @correct_num_answer;
  446   my ($PG_eval_errors,$PG_full_error_report);
  447   assign_option_aliases( \%mult_params,
  448                 'reltol'    =>      'relTol',
  449       );
  450       set_default_options(\%mult_params,
  451               'tolType'   =>  (defined($mult_params{tol}) ) ? 'absolute' : 'relative',
  452               # default mode should be relative, to obtain this tol must not be defined
  453           'tolerance'   =>  $main::numAbsTolDefault,
  454                       'relTol'    =>  $main::numRelPercentTolDefault,
  455           'zeroLevel'   =>  $main::numZeroLevelDefault,
  456           'zeroLevelTol'    =>  $main::numZeroLevelTolDefault,
  457           'format'    =>  $main::numFormatDefault,
  458           'debug'     =>    0,
  459           'mode'      =>  'std',
  460           'compare'   =>  'num',
  461   );
  462   my $format    = $mult_params{'format'};
  463   my $mode    = $mult_params{'mode'};
  464 
  465   if( $mult_params{tolType} eq 'relative' ) {
  466     $mult_params{'tolerance'} = .01*$mult_params{'tolerance'};
  467   }
  468 
  469   if( $mult_params{ 'compare' } eq 'cplx' ){
  470     foreach( @answers )
  471     {
  472       $_ = cplx( $_, 0 ) unless ref($_) =~/Complex/;
  473     }
  474   }
  475 
  476   my $corrAnswerIsString = 0;
  477 
  478   for( my $k = 0; $k < $number_of_answers; $k++ ){
  479   if (defined($mult_params{strings}) && $mult_params{strings}) {
  480     my $legalString = '';
  481     my @legalStrings = @{$mult_params{strings}};
  482     $correct_num_answer[$k] = $answers[$k];
  483     $formattedCorrectAnswer .= $answers[$k] . ",";
  484     foreach $legalString (@legalStrings) {
  485       if ( uc($answers[$k]) eq uc($legalString) ) {
  486         $corrAnswerIsString = 1;
  487 
  488         last;
  489       }
  490     }   ## at this point $corrAnswerIsString = 0 iff correct answer is numeric
  491   } else {
  492     $correct_num_answer[$k] = $answers[$k];
  493       $formattedCorrectAnswer .= prfmt( $answers[$k], $mult_params{'format'} ) . ", ";
  494   }
  495   $correct_num_answer[$k] = math_constants($correct_num_answer[$k]);
  496   my $PGanswerMessage = '';
  497 
  498 
  499   if (defined($correct_num_answer[$k]) && $correct_num_answer[$k] =~ /\S/ && $corrAnswerIsString == 0 ) {
  500       ($correctVal[$k], $PG_eval_errors,$PG_full_error_report) =
  501       PG_answer_eval($correct_num_answer[$k]);
  502   } else { # case of a string answer
  503     $PG_eval_errors = ' ';
  504     $correctVal[$k] = $answers[$k];
  505   }
  506 
  507   #if ( ($PG_eval_errors && $corrAnswerIsString == 0) or ((not is_a_number($correctVal[$k])) && $corrAnswerIsString == 0)) {
  508         ##error message from eval or above
  509     #warn "Error in 'correct' answer: $PG_eval_errors<br>
  510           #The answer $answers[$k] evaluates to $correctVal[$k],
  511           #which cannot be interpreted as a number.  ";
  512 
  513   #}
  514   ########################################################################
  515   $correctVal[$k] = $correct_num_answer[$k];
  516   }
  517   $formattedCorrectAnswer =~ s/, \Z//;
  518 
  519   #construct the answer evaluator
  520 
  521       my $answer_evaluator = new AnswerEvaluator;
  522 
  523 
  524       $answer_evaluator->{debug} = $mult_params{debug};
  525       $answer_evaluator->ans_hash(
  526                 correct_ans       =>  [@correctVal],
  527                 type        =>  "${mode}_number",
  528                 tolerance     =>  $mult_params{tolerance},
  529             tolType       =>  'absolute', # $mult_params{tolType},
  530             original_correct_ans    =>  $formattedCorrectAnswer,
  531                 answerIsString      =>  $corrAnswerIsString,
  532             answer_form     =>  'cartesian',
  533       );
  534       my ($in, $formattedSubmittedAnswer);
  535     $answer_evaluator->install_pre_filter(sub {my $rh_ans = shift;
  536     $rh_ans->{original_student_ans} = $rh_ans->{student_ans}; $rh_ans;}
  537   );
  538   if (defined($mult_params{strings}) && $mult_params{strings}) {
  539       $answer_evaluator->install_pre_filter(\&check_strings, %mult_params);
  540   }
  541 
  542   $answer_evaluator -> install_pre_filter( \&mult_prefilters, %mult_params );
  543   $answer_evaluator->install_pre_filter( sub{my $rh_ans = shift; $rh_ans->{original_student_ans} = $rh_ans->{student_ans};$rh_ans;});
  544 
  545   if ($corrAnswerIsString == 0 ){   # avoiding running compare_numbers when correct answer is a string.
  546     $answer_evaluator->install_evaluator(\&compare_mult, %mult_params);
  547   }
  548 
  549 
  550 ###############################################################################
  551 # We'll leave these next lines out for now, so that the evaluated versions of the student's and professor's
  552 # can be displayed in the answer message.  This may still cause a few anomolies when strings are used
  553 #
  554 ###############################################################################
  555   $answer_evaluator->install_post_filter( sub{my $rh_ans = shift; $rh_ans->{student_ans} = $rh_ans->{original_student_ans};$rh_ans;});
  556   $answer_evaluator->install_post_filter(\&fix_answers_for_display);
  557   $answer_evaluator->install_post_filter(\&fix_for_polar_display);
  558 
  559       $answer_evaluator->install_post_filter(sub {my $rh_ans = shift;
  560           return $rh_ans unless $rh_ans->catch_error('EVAL');
  561           $rh_ans->{student_ans} = $rh_ans->{original_student_ans}. ' '. $rh_ans->{error_message};
  562           $rh_ans->clear_error('EVAL'); } );
  563       $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('SYNTAX'); } );
  564       $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('POLAR'); } );
  565       $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('CARTESIAN'); } );
  566       $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('COMPLEX'); } );
  567   $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('STRING'); } );
  568       $answer_evaluator;
  569 }
  570 
  571 sub mult_prefilters{
  572   my ($rh_ans, %options) = @_;
  573   my @student_answers = split/,/,$rh_ans->{student_ans};
  574   foreach( @student_answers ){
  575     $rh_ans->{student_ans} = $_;
  576     $rh_ans = &check_syntax( $rh_ans );
  577     $rh_ans = &math_constants( $rh_ans );
  578     if( $options{compare} eq 'cplx' ){
  579       $rh_ans = &cplx_constants( $rh_ans );
  580       #$rh_ans = &check_for_polar( $rh_ans );
  581     }
  582     if ( $options{mode} eq 'std') {
  583         # do nothing
  584     } elsif ($options{mode} eq 'strict_polar') {
  585       $rh_ans = &is_a_polar( $rh_ans );
  586     } elsif ($options{mode} eq 'strict_num_cartesian') {
  587       $rh_ans = &is_a_numeric_cartesian( $rh_ans );
  588     } elsif ($options{mode} eq 'strict_num_polar') {
  589       $rh_ans = &is_a_numeric_polar( $rh_ans );
  590     } elsif ($options{mode} eq 'strict') {
  591       $rh_ans = &is_a_numeric_complex( $rh_ans );
  592     } elsif ($options{mode} eq 'arith') {
  593       $rh_ans = &is_an_arithmetic_expression( $rh_ans );
  594     } elsif ($options{mode} eq 'frac') {
  595       $rh_ans = &is_a_fraction( $rh_ans );
  596 
  597     } else {
  598       #$PGanswerMessage = 'Tell your professor  that there is an error in his or her answer mechanism. No mode was specified.';
  599       #$formattedSubmittedAnswer =  $in;
  600     }
  601     $_ = $rh_ans->{student_ans};
  602   }
  603   my $ans_string;
  604   foreach( @student_answers )
  605   {
  606     $ans_string .= ", $_";
  607   }
  608   $ans_string =~ s/\A,//;
  609   $rh_ans->{student_ans} = $ans_string;
  610   $rh_ans;
  611 }
  612 
  613 # compares two complex numbers by comparing their real and imaginary parts
  614 sub compare_mult {
  615   my ($rh_ans, %options) = @_;
  616   my @student_answers = split/,/,$rh_ans->{student_ans};
  617   my @correct_answers = @{$rh_ans->{correct_ans}};
  618   my $one_correct = 1/@correct_answers;
  619   my $temp_score = 0;
  620   foreach( @correct_answers ){
  621     $rh_ans->{correct_ans} = $_;
  622     foreach( @student_answers ){
  623       $rh_ans->{student_ans} = $_;
  624       if( $options{compare} eq 'cplx' ){
  625         $rh_ans = &compare_cplx( $rh_ans, %options);
  626       }else{
  627         $rh_ans = &compare_numbers( $rh_ans, %options);
  628       }
  629       if( $rh_ans->{score} == 1 )
  630       {
  631         $temp_score += $one_correct;
  632         $rh_ans->{score} = 0;
  633         last;
  634       }
  635     }
  636   }
  637   $rh_ans->{score} = $temp_score;
  638   $rh_ans;
  639 
  640 }
  641 
  642 
  643 
  644 #this basically just checks for "e^" which unfortunately will show something like (e^4)*i as a polar, this should be changed
  645 sub check_for_polar{
  646 
  647   my($in,%options) = @_;
  648   my $rh_ans;
  649   my $process_ans_hash = ( ref( $in ) eq 'AnswerHash' ) ? 1 : 0 ;
  650   if ($process_ans_hash) {
  651     $rh_ans = $in;
  652     $in = $rh_ans->{student_ans};
  653   }
  654   # The code fragment above allows this filter to be used when the input is simply a string
  655   # as well as when the input is an AnswerHash, and options.
  656   if( $in =~ /2.71828182845905\*\*/ ){
  657   $rh_ans->{answer_form} = 'polar';
  658   }
  659   $rh_ans;
  660 }
  661 
  662 
  663 # compares two complex numbers by comparing their real and imaginary parts
  664 sub compare_cplx {
  665   my ($rh_ans, %options) = @_;
  666   my ($inVal,$PG_eval_errors,$PG_full_error_report) = PG_answer_eval($rh_ans->{student_ans});
  667 
  668   if ($PG_eval_errors) {
  669     $rh_ans->throw_error('EVAL','There is a syntax error in your answer');
  670     $rh_ans->{ans_message} = clean_up_error_msg($PG_eval_errors);
  671     # return $rh_ans;
  672   } else {
  673     $rh_ans->{student_ans} = prfmt($inVal,$options{format});
  674   }
  675 
  676   $inVal = cplx($inVal,0) unless ref($inVal) =~/Complex/;
  677   my $permitted_error_Re;
  678   my $permitted_error_Im;
  679   if ($rh_ans->{tolType} eq 'absolute') {
  680     $permitted_error_Re = $rh_ans->{tolerance};
  681     $permitted_error_Im = $rh_ans->{tolerance};
  682   }
  683   elsif ( abs($rh_ans->{correct_ans}) <= $options{zeroLevel}) {
  684       $permitted_error_Re = $options{zeroLevelTol};  ## want $tol to be non zero
  685       $permitted_error_Im = $options{zeroLevelTol};  ## want $tol to be non zero
  686   }
  687   else {
  688     $permitted_error_Re =  abs($rh_ans->{tolerance}*$rh_ans->{correct_ans}->Complex::Re);
  689     $permitted_error_Im =  abs($rh_ans->{tolerance}*$rh_ans->{correct_ans}->Complex::Im);
  690 
  691   }
  692 
  693   $rh_ans->{score} = 1 if ( abs( $rh_ans->{correct_ans}->Complex::Re - $inVal->Complex::Re) <=
  694   $permitted_error_Re && abs($rh_ans->{correct_ans}->Complex::Im - $inVal->Complex::Im )<= $permitted_error_Im  );
  695 
  696   $rh_ans;
  697 }
  698 
  699 
  700 sub cplx_constants {
  701   my($in,%options) = @_;
  702   my $rh_ans;
  703   my $process_ans_hash = ( ref( $in ) eq 'AnswerHash' ) ? 1 : 0 ;
  704   if ($process_ans_hash) {
  705     $rh_ans = $in;
  706     $in = $rh_ans->{student_ans};
  707   }
  708   # The code fragment above allows this filter to be used when the input is simply a string
  709   # as well as when the input is an AnswerHash, and options.
  710   $in =~ s/\bi\b/(i)/g;  #try to keep -i being recognized as a file reference
  711                                                            # and recognized as a function whose output is an imaginary number
  712 
  713   if ($process_ans_hash)   {
  714       $rh_ans->{student_ans}=$in;
  715       return $rh_ans;
  716     } else {
  717     return $in;
  718   }
  719 }
  720 
  721 ## allows only for numbers of the form a+bi and ae^(bi), where a and b are strict numbers
  722 sub is_a_numeric_complex {
  723   my ($num,%options) =  @_;
  724   my $process_ans_hash = ( ref( $num ) eq 'AnswerHash' ) ? 1 : 0 ;
  725   my ($rh_ans);
  726   if ($process_ans_hash) {
  727     $rh_ans = $num;
  728     $num = $rh_ans->{student_ans};
  729   }
  730 
  731   my $is_a_number = 0;
  732   return $is_a_number unless defined($num);
  733   $num =~ s/^\s*//; ## remove initial spaces
  734   $num =~ s/\s*$//; ## remove trailing spaces
  735 
  736   if ($num =~
  737 
  738 /^($number[+,-]?($number\*\(i\)|\(i\)|\(i\)\*$number)|($number\*\(i\)|-?\(i\)|-?\(i\)\*$number)([+,-]$number)?|($number\*)?2.71828182845905\*\*\(($number\*\(i\)|\(i\)\*$number|i|-\(i\))\)|$number)$/){
  739     $is_a_number = 1;
  740   }
  741 
  742   if ($process_ans_hash)   {
  743         if ($is_a_number == 1 ) {
  744           $rh_ans->{student_ans}=$num;
  745           return $rh_ans;
  746         } else {
  747           $rh_ans->{student_ans} = "Incorrect number format:  You must enter a numeric complex, e.g. a+bi
  748       or a*e^(bi)";
  749           $rh_ans->throw_error('COMPLEX', 'You must enter a number, e.g. -6, 5.3, or 6.12E-3');
  750           return $rh_ans;
  751         }
  752   } else {
  753     return $is_a_number;
  754   }
  755 }
  756 
  757 ## allows only for the form a + bi, where a and b are strict numbers
  758 sub is_a_numeric_cartesian {
  759   my ($num,%options) =  @_;
  760   my $process_ans_hash = ( ref( $num ) eq 'AnswerHash' ) ? 1 : 0 ;
  761   my ($rh_ans);
  762   if ($process_ans_hash) {
  763     $rh_ans = $num;
  764     $num = $rh_ans->{student_ans};
  765   }
  766 
  767   my $is_a_number = 0;
  768   return $is_a_number unless defined($num);
  769   $num =~ s/^\s*//; ## remove initial spaces
  770   $num =~ s/\s*$//; ## remove trailing spaces
  771 
  772   if ($num =~
  773 
  774 /^($number[+,-]?($number\*\(i\)|\(i\)|\(i\)\*$number)|($number\*\(i\)|-?\(i\)|-?\(i\)\*$number)([+,-]$number)?|$number)$/){
  775     $is_a_number = 1;
  776   }
  777 
  778   if ($process_ans_hash)   {
  779         if ($is_a_number == 1 ) {
  780           $rh_ans->{student_ans}=$num;
  781           return $rh_ans;
  782         } else {
  783           $rh_ans->{student_ans} = "Incorrect number format:  You must enter a numeric cartesian, e.g. a+bi";
  784           $rh_ans->throw_error('CARTESIAN', 'You must enter a number, e.g. -6, 5.3, or 6.12E-3');
  785           return $rh_ans;
  786         }
  787   } else {
  788     return $is_a_number;
  789   }
  790 }
  791 
  792 ## allows only for the form ae^(bi), where a and b are strict numbers
  793 sub is_a_numeric_polar {
  794   my ($num,%options) =  @_;
  795   my $process_ans_hash = ( ref( $num ) eq 'AnswerHash' ) ? 1 : 0 ;
  796   my ($rh_ans);
  797   if ($process_ans_hash) {
  798     $rh_ans = $num;
  799     $num = $rh_ans->{student_ans};
  800   }
  801 
  802   my $is_a_number = 0;
  803   return $is_a_number unless defined($num);
  804   $num =~ s/^\s*//; ## remove initial spaces
  805   $num =~ s/\s*$//; ## remove trailing spaces
  806   if ($num =~
  807   /^($number|($number\*)?2.71828182845905\*\*\(($number\*\(i\)|\(i\)\*$number|i|-\(i\))\))$/){
  808     $is_a_number = 1;
  809   }
  810 
  811   if ($process_ans_hash)   {
  812         if ($is_a_number == 1 ) {
  813           $rh_ans->{student_ans}=$num;
  814           return $rh_ans;
  815         } else {
  816           $rh_ans->{student_ans} = "Incorrect number format:  You must enter a numeric polar, e.g. a*e^(bi)";
  817           $rh_ans->throw_error('POLAR', 'You must enter a number, e.g. -6, 5.3, or 6.12E-3');
  818           return $rh_ans;
  819         }
  820   } else {
  821     return $is_a_number;
  822   }
  823 }
  824 #this subroutine mearly captures what is before and after the "e**" it does not verify that the "i" is there, or in the
  825 #exponent this must eventually be addresed
  826 sub is_a_polar {
  827   my ($num,%options) =  @_;
  828   my $process_ans_hash = ( ref( $num ) eq 'AnswerHash' ) ? 1 : 0 ;
  829   my ($rh_ans);
  830   if ($process_ans_hash) {
  831     $rh_ans = $num;
  832     $num = $rh_ans->{student_ans};
  833   }
  834 
  835   my $is_a_number = 0;
  836   return $is_a_number unless defined($num);
  837   $num =~ s/^\s*//; ## remove initial spaces
  838   $num =~ s/\s*$//; ## remove trailing spaces
  839   $num =~ /^(.*)\*2.71828182845905\*\*(.*)/;
  840   #warn "rho: ", $1;
  841   #warn "theta: ", $2;
  842   if( defined( $1 ) ){
  843     if( &single_term( $1 ) && &single_term( $2 ) )
  844     {
  845       $is_a_number = 1;
  846     }
  847   }
  848   if ($process_ans_hash)   {
  849         if ($is_a_number == 1 ) {
  850           $rh_ans->{student_ans}=$num;
  851           return $rh_ans;
  852         } else {
  853           $rh_ans->{student_ans} = "Incorrect number format:  You must enter a polar, e.g. a*e^(bi)";
  854           $rh_ans->throw_error('POLAR', 'You must enter a number, e.g. -6, 5.3, or 6.12E-3');
  855           return $rh_ans;
  856         }
  857   } else {
  858     return $is_a_number;
  859   }
  860 }
  861 
  862 =head4 single_term()
  863   This subroutine takes in a string, which is a mathematical expresion, and determines whether or not
  864   it is a single term. This is accoplished using a stack. Open parenthesis pluses and minuses are all
  865   added onto the stack, and when a closed parenthesis is reached, the stack is popped untill the open
  866   parenthesis is found. If the original was a single term, the stack should be empty after
  867   evaluation. If there is anything left ( + or - ) then false is returned.
  868   Of course, the unary operator "-" must be handled... if it is a unary operator, and not a regular -
  869   the only place it could occur unambiguously without being surrounded by parenthesis, is the very
  870   first position. So that case is checked before the loop begins.
  871 =cut
  872 
  873 sub single_term{
  874   my $term = shift;
  875   my @stack;
  876   $term = reverse $term;
  877   if( length $term >= 1 )
  878   {
  879     my $temp = chop $term;
  880     if( $temp ne "-" ){ $term .= $temp; }
  881   }
  882   while( length $term >= 1 ){
  883     my $character = chop $term;
  884     if( $character eq "+" || $character eq "-" || $character eq "(" ){
  885       push @stack, $character;
  886     }elsif( $character eq ")" ){
  887       while( pop @stack ne "(" ){}
  888     }
  889 
  890   }
  891   if( scalar @stack == 0 ){ return 1;}else{ return 0;}
  892 }
  893 
  894 # changes default to display as a polar
  895 sub fix_for_polar_display{
  896   my ($rh_ans, %options) = @_;
  897   if( ref( $rh_ans->{student_ans} ) =~ /Complex/ && $rh_ans->{answer_form} eq 'polar' ){
  898     $rh_ans->{student_ans}->display_format( 'polar');
  899     ## these lines of code have the polar displayed as re^(theta) instead of [rho,theta]
  900     $rh_ans->{student_ans} =~ s/,/*e^\(/;
  901     $rh_ans->{student_ans} =~ s/\[//;
  902     $rh_ans->{student_ans} =~ s/\]/i\)/;
  903     }
  904   $rh_ans;
  905 }
  906 
  907 sub cplx_cmp2 {
  908   my $correctAnswer = shift;
  909   my %cplx_params = @_;
  910   my @keys = qw ( correctAnswer tolerance tolType format mode zeroLevel zeroLevelTol debug );
  911   assign_option_aliases( \%cplx_params,
  912                 'reltol'    =>      'relTol',
  913       );
  914       set_default_options(\%cplx_params,
  915               'tolType'   =>  (defined($cplx_params{tol}) ) ? 'absolute' : 'relative',
  916               # default mode should be relative, to obtain this tol must not be defined
  917           'tolerance'   =>  $main::numAbsTolDefault,
  918                       'relTol'    =>  $main::numRelPercentTolDefault,
  919           'zeroLevel'   =>  $main::numZeroLevelDefault,
  920           'zeroLevelTol'    =>  $main::numZeroLevelTolDefault,
  921           'format'    =>  $main::numFormatDefault,
  922           'debug'     =>    0,
  923           'mode'      =>  'std',
  924 
  925       );
  926   $correctAnswer = cplx($correctAnswer,0) unless ref($correctAnswer) =~/Complex/;
  927   my $format    = $cplx_params{'format'};
  928   my $mode    = $cplx_params{'mode'};
  929 
  930   if( $cplx_params{tolType} eq 'relative' ) {
  931     $cplx_params{'tolerance'} = .01*$cplx_params{'tolerance'};
  932   }
  933 
  934   my $formattedCorrectAnswer;
  935   my $correct_num_answer;
  936   my $corrAnswerIsString = 0;
  937 
  938 
  939   if (defined($cplx_params{strings}) && $cplx_params{strings}) {
  940     my $legalString = '';
  941     my @legalStrings = @{$cplx_params{strings}};
  942     $correct_num_answer = $correctAnswer;
  943     $formattedCorrectAnswer = $correctAnswer;
  944     foreach $legalString (@legalStrings) {
  945       if ( uc($correctAnswer) eq uc($legalString) ) {
  946         $corrAnswerIsString = 1;
  947 
  948         last;
  949       }
  950     }     ## at this point $corrAnswerIsString = 0 iff correct answer is numeric
  951   } else {
  952     $correct_num_answer = $correctAnswer;
  953     $formattedCorrectAnswer = prfmt( $correctAnswer, $cplx_params{'format'} );
  954   }
  955   $correct_num_answer = math_constants($correct_num_answer);
  956   my $PGanswerMessage = '';
  957 
  958   my ($inVal,$correctVal,$PG_eval_errors,$PG_full_error_report);
  959 
  960   if (defined($correct_num_answer) && $correct_num_answer =~ /\S/ && $corrAnswerIsString == 0 ) {
  961       ($correctVal, $PG_eval_errors,$PG_full_error_report) = PG_answer_eval($correct_num_answer);
  962   } else { # case of a string answer
  963     $PG_eval_errors = ' ';
  964     $correctVal = $correctAnswer;
  965   }
  966 
  967   if ( ($PG_eval_errors && $corrAnswerIsString == 0) or ((not is_a_number($correctVal)) && $corrAnswerIsString == 0)) {
  968         ##error message from eval or above
  969     warn "Error in 'correct' answer: $PG_eval_errors<br>
  970           The answer $correctAnswer evaluates to $correctVal,
  971           which cannot be interpreted as a number.  ";
  972 
  973   }
  974   ########################################################################
  975   $correctVal = $correct_num_answer;#it took me two and a half hours to figure out that correctVal wasn't
  976   #getting the number properly
  977   #construct the answer evaluator
  978       my $answer_evaluator = new AnswerEvaluator;
  979 
  980 
  981       $answer_evaluator->{debug} = $cplx_params{debug};
  982       $answer_evaluator->ans_hash(
  983                 correct_ans       =>  $correctVal,
  984                 type        =>  "${mode}_number",
  985                 tolerance     =>  $cplx_params{tolerance},
  986             tolType       =>  'absolute', # $cplx_params{tolType},
  987             original_correct_ans    =>  $formattedCorrectAnswer,
  988                 answerIsString      =>  $corrAnswerIsString,
  989             answer_form     =>  'cartesian',
  990       );
  991       my ($in, $formattedSubmittedAnswer);
  992     $answer_evaluator->install_pre_filter(sub {my $rh_ans = shift;
  993     $rh_ans->{original_student_ans} = $rh_ans->{student_ans}; $rh_ans;}
  994   );
  995   if (defined($cplx_params{strings}) && $cplx_params{strings}) {
  996       $answer_evaluator->install_pre_filter(\&check_strings, %cplx_params);
  997   }
  998   #$answer_evaluator->install_pre_filter(\&check_syntax);
  999 
 1000   $answer_evaluator->install_pre_filter(\&math_constants);
 1001   $answer_evaluator->install_pre_filter(\&cplx_constants);
 1002   $answer_evaluator->install_pre_filter(\&check_for_polar);
 1003   if ($mode eq 'std') {
 1004         # do nothing
 1005   } elsif ($mode eq 'strict_polar') {
 1006     $answer_evaluator->install_pre_filter(\&is_a_polar);
 1007   } elsif ($mode eq 'strict_num_cartesian') {
 1008     $answer_evaluator->install_pre_filter(\&is_a_numeric_cartesian);
 1009   } elsif ($mode eq 'strict_num_polar') {
 1010     $answer_evaluator->install_pre_filter(\&is_a_numeric_polar);
 1011   } elsif ($mode eq 'strict') {
 1012     $answer_evaluator->install_pre_filter(\&is_a_numeric_complex);
 1013   } elsif ($mode eq 'arith') {
 1014       $answer_evaluator->install_pre_filter(\&is_an_arithmetic_expression);
 1015     } elsif ($mode eq 'frac') {
 1016       $answer_evaluator->install_pre_filter(\&is_a_fraction);
 1017 
 1018     } else {
 1019       $PGanswerMessage = 'Tell your professor that there is an error in his or her answer mechanism. No mode was specified.';
 1020       $formattedSubmittedAnswer = $in;
 1021     }
 1022   if ($corrAnswerIsString == 0 ){   # avoiding running compare_numbers when correct answer is a string.
 1023     $answer_evaluator->install_evaluator(\&compare_cplx2, %cplx_params);
 1024   }
 1025 
 1026 
 1027 ###############################################################################
 1028 # We'll leave these next lines out for now, so that the evaluated versions of the student's and professor's
 1029 # can be displayed in the answer message.  This may still cause a few anomolies when strings are used
 1030 #
 1031 ###############################################################################
 1032 
 1033   $answer_evaluator->install_post_filter(\&fix_answers_for_display);
 1034   $answer_evaluator->install_post_filter(\&fix_for_polar_display);
 1035 
 1036       $answer_evaluator->install_post_filter(sub {my $rh_ans = shift;
 1037           return $rh_ans unless $rh_ans->catch_error('EVAL');
 1038           $rh_ans->{student_ans} = $rh_ans->{original_student_ans}. ' '. $rh_ans->{error_message};
 1039           $rh_ans->clear_error('EVAL'); } );
 1040       $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('SYNTAX'); } );
 1041       $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('POLAR'); } );
 1042       $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('CARTESIAN'); } );
 1043       $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('COMPLEX'); } );
 1044   $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('STRING'); } );
 1045       $answer_evaluator;
 1046 }
 1047 
 1048 
 1049 # compares two complex numbers by comparing their real and imaginary parts
 1050 sub compare_cplx2 {
 1051   my ($rh_ans, %options) = @_;
 1052   my @answers = split/,/,$rh_ans->{student_ans};
 1053   foreach( @answers )
 1054   {
 1055   $rh_ans->{student_ans} = $_;
 1056   $rh_ans = &check_syntax( $rh_ans );
 1057   my ($inVal,$PG_eval_errors,$PG_full_error_report) = PG_answer_eval($rh_ans->{student_ans});
 1058 
 1059   if ($PG_eval_errors) {
 1060     $rh_ans->throw_error('EVAL','There is a syntax error in your answer');
 1061     $rh_ans->{ans_message} = clean_up_error_msg($PG_eval_errors);
 1062     # return $rh_ans;
 1063   } else {
 1064     $rh_ans->{student_ans} = prfmt($inVal,$options{format});
 1065   }
 1066 
 1067   $inVal = cplx($inVal,0) unless ref($inVal) =~/Complex/;
 1068   my $permitted_error_Re;
 1069   my $permitted_error_Im;
 1070   if ($rh_ans->{tolType} eq 'absolute') {
 1071     $permitted_error_Re = $rh_ans->{tolerance};
 1072     $permitted_error_Im = $rh_ans->{tolerance};
 1073   }
 1074   elsif ( abs($rh_ans->{correct_ans}) <= $options{zeroLevel}) {
 1075       $permitted_error_Re = $options{zeroLevelTol};  ## want $tol to be non zero
 1076       $permitted_error_Im = $options{zeroLevelTol};  ## want $tol to be non zero
 1077   }
 1078   else {
 1079     $permitted_error_Re =  abs($rh_ans->{tolerance}*$rh_ans->{correct_ans}->Complex::Re);
 1080     $permitted_error_Im =  abs($rh_ans->{tolerance}*$rh_ans->{correct_ans}->Complex::Im);
 1081 
 1082   }
 1083 
 1084   $rh_ans->{score} = 1 if ( abs( $rh_ans->{correct_ans}->Complex::Re - $inVal->Complex::Re) <=
 1085   $permitted_error_Re && abs($rh_ans->{correct_ans}->Complex::Im - $inVal->Complex::Im )<= $permitted_error_Im  );
 1086   if( $rh_ans->{score} == 1 ){ return $rh_ans; }
 1087 
 1088 
 1089   }
 1090   $rh_ans;
 1091 
 1092 }
 1093 
 1094 
 1095 sub cplx_cmp_mult {
 1096   my $correctAnswer = shift;
 1097   my %cplx_params = @_;
 1098   my @keys = qw ( correctAnswer tolerance tolType format mode zeroLevel zeroLevelTol debug );
 1099   assign_option_aliases( \%cplx_params,
 1100                 'reltol'    =>      'relTol',
 1101       );
 1102       set_default_options(\%cplx_params,
 1103               'tolType'   =>  (defined($cplx_params{tol}) ) ? 'absolute' : 'relative',
 1104               # default mode should be relative, to obtain this tol must not be defined
 1105           'tolerance'   =>  $main::numAbsTolDefault,
 1106                       'relTol'    =>  $main::numRelPercentTolDefault,
 1107           'zeroLevel'   =>  $main::numZeroLevelDefault,
 1108           'zeroLevelTol'    =>  $main::numZeroLevelTolDefault,
 1109           'format'    =>  $main::numFormatDefault,
 1110           'debug'     =>    0,
 1111           'mode'      =>  'std',
 1112 
 1113       );
 1114   $correctAnswer = cplx($correctAnswer,0) unless ref($correctAnswer) =~/Complex/;
 1115   my $format    = $cplx_params{'format'};
 1116   my $mode    = $cplx_params{'mode'};
 1117 
 1118   if( $cplx_params{tolType} eq 'relative' ) {
 1119     $cplx_params{'tolerance'} = .01*$cplx_params{'tolerance'};
 1120   }
 1121 
 1122   my $formattedCorrectAnswer;
 1123   my $correct_num_answer;
 1124   my $corrAnswerIsString = 0;
 1125 
 1126 
 1127   if (defined($cplx_params{strings}) && $cplx_params{strings}) {
 1128     my $legalString = '';
 1129     my @legalStrings = @{$cplx_params{strings}};
 1130     $correct_num_answer = $correctAnswer;
 1131     $formattedCorrectAnswer = $correctAnswer;
 1132     foreach $legalString (@legalStrings) {
 1133       if ( uc($correctAnswer) eq uc($legalString) ) {
 1134         $corrAnswerIsString = 1;
 1135 
 1136         last;
 1137       }
 1138     }     ## at this point $corrAnswerIsString = 0 iff correct answer is numeric
 1139   } else {
 1140     $correct_num_answer = $correctAnswer;
 1141     $formattedCorrectAnswer = prfmt( $correctAnswer, $cplx_params{'format'} );
 1142   }
 1143   $correct_num_answer = math_constants($correct_num_answer);
 1144   my $PGanswerMessage = '';
 1145 
 1146   my ($inVal,$correctVal,$PG_eval_errors,$PG_full_error_report);
 1147 
 1148   if (defined($correct_num_answer) && $correct_num_answer =~ /\S/ && $corrAnswerIsString == 0 ) {
 1149       ($correctVal, $PG_eval_errors,$PG_full_error_report) = PG_answer_eval($correct_num_answer);
 1150   } else { # case of a string answer
 1151     $PG_eval_errors = ' ';
 1152     $correctVal = $correctAnswer;
 1153   }
 1154 
 1155   if ( ($PG_eval_errors && $corrAnswerIsString == 0) or ((not is_a_number($correctVal)) && $corrAnswerIsString == 0)) {
 1156         ##error message from eval or above
 1157     warn "Error in 'correct' answer: $PG_eval_errors<br>
 1158           The answer $correctAnswer evaluates to $correctVal,
 1159           which cannot be interpreted as a number.  ";
 1160 
 1161   }
 1162   ########################################################################
 1163   $correctVal = $correct_num_answer;#it took me two and a half hours to figure out that correctVal wasn't
 1164   #getting the number properly
 1165   #construct the answer evaluator
 1166   my $counter = 0;
 1167   my $answer_evaluator = new AnswerEvaluator;
 1168 
 1169   my $number;
 1170   $answer_evaluator->install_pre_filter( sub{ my $rh_ans = shift; my @temp =
 1171   split/,/,$rh_ans->{student_ans}; $number = @temp; warn "this number ", $number; $rh_ans;});
 1172   warn "number ", $number;
 1173   while( $counter < 4 )
 1174   {
 1175   $answer_evaluator = &answer_mult( $correctVal, $mode, $formattedCorrectAnswer,
 1176   $corrAnswerIsString, $counter, %cplx_params );
 1177   warn "answer_evaluator ", $answer_evaluator;
 1178   $answer_evaluator->install_evaluator( sub { my $rh_ans = shift; warn "score ", $rh_ans->{score};
 1179   $rh_ans;});
 1180   $counter += 1;
 1181   }
 1182 
 1183   $answer_evaluator;
 1184 
 1185 }
 1186 
 1187 sub answer_mult{
 1188   my $correctVal = shift;
 1189   my $mode = shift;
 1190   my $formattedCorrectAnswer = shift;
 1191   my $corrAnswerIsString = shift;
 1192   my $counter = shift;
 1193   warn "counter ", $counter;
 1194 
 1195   my %cplx_params = @_;
 1196       my $answer_evaluator = new AnswerEvaluator;
 1197 
 1198 
 1199       $answer_evaluator->{debug} = $cplx_params{debug};
 1200       $answer_evaluator->ans_hash(
 1201                 correct_ans       =>  $correctVal,
 1202                 type        =>  "${mode}_number",
 1203                 tolerance     =>  $cplx_params{tolerance},
 1204             tolType       =>  'absolute', # $cplx_params{tolType},
 1205             original_correct_ans    =>  $formattedCorrectAnswer,
 1206                 answerIsString      =>  $corrAnswerIsString,
 1207             answer_form     =>  'cartesian',
 1208       );
 1209   $answer_evaluator->install_pre_filter(sub {
 1210     my $rh_ans = shift;
 1211     $rh_ans->{original_student_ans} = $rh_ans->{student_ans};
 1212     my @answers = split/,/,$rh_ans->{student_ans};
 1213     $rh_ans -> {student_ans} = $answers[$counter];
 1214     $rh_ans;
 1215     }
 1216   );
 1217   if (defined($cplx_params{strings}) && $cplx_params{strings}) {
 1218       $answer_evaluator->install_pre_filter(\&check_strings, %cplx_params);
 1219   }
 1220   $answer_evaluator->install_pre_filter(\&check_syntax);
 1221   $answer_evaluator->install_pre_filter(\&math_constants);
 1222   $answer_evaluator->install_pre_filter(\&cplx_constants);
 1223   $answer_evaluator->install_pre_filter(\&check_for_polar);
 1224   if ($mode eq 'std') {
 1225         # do nothing
 1226   } elsif ($mode eq 'strict_polar') {
 1227     $answer_evaluator->install_pre_filter(\&is_a_polar);
 1228   } elsif ($mode eq 'strict_num_cartesian') {
 1229     $answer_evaluator->install_pre_filter(\&is_a_numeric_cartesian);
 1230   } elsif ($mode eq 'strict_num_polar') {
 1231     $answer_evaluator->install_pre_filter(\&is_a_numeric_polar);
 1232   } elsif ($mode eq 'strict') {
 1233     $answer_evaluator->install_pre_filter(\&is_a_numeric_complex);
 1234   } elsif ($mode eq 'arith') {
 1235       $answer_evaluator->install_pre_filter(\&is_an_arithmetic_expression);
 1236     } elsif ($mode eq 'frac') {
 1237       $answer_evaluator->install_pre_filter(\&is_a_fraction);
 1238 
 1239     } else {
 1240       #$PGanswerMessage = 'Tell your professor  that there is an error in his or her answer mechanism. No mode was specified.';
 1241     }
 1242   if ($corrAnswerIsString == 0 ){   # avoiding running compare_numbers when correct answer is a string.
 1243     $answer_evaluator->install_evaluator(\&compare_cplx, %cplx_params);
 1244   }
 1245 
 1246 
 1247 ###############################################################################
 1248 # We'll leave these next lines out for now, so that the evaluated versions of the student's and professor's
 1249 # can be displayed in the answer message.  This may still cause a few anomolies when strings are used
 1250 #
 1251 ###############################################################################
 1252 
 1253   $answer_evaluator->install_post_filter(\&fix_answers_for_display);
 1254   $answer_evaluator->install_post_filter(\&fix_for_polar_display);
 1255       $answer_evaluator->install_post_filter(sub {my $rh_ans = shift;
 1256           return $rh_ans unless $rh_ans->catch_error('EVAL');
 1257           $rh_ans->{student_ans} = $rh_ans->{original_student_ans}. ' '. $rh_ans->{error_message};
 1258           $rh_ans->clear_error('EVAL'); } );
 1259       $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('SYNTAX'); } );
 1260       $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('POLAR'); } );
 1261       $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('CARTESIAN'); } );
 1262       $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('COMPLEX'); } );
 1263   $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; warn "ans hash", $rh_ans->clear_error('STRING'); } );
 1264   $answer_evaluator;
 1265 }
 1266 
 1267 
 1268 
 1269 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9