[system] / trunk / pg / macros / PGcomplexmacros.pl Repository:
ViewVC logotype

View of /trunk/pg/macros/PGcomplexmacros.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2986 - (download) (as text) (annotate)
Thu Nov 4 22:01:18 2004 UTC (15 years, 1 month ago) by apizer
File size: 42141 byte(s)
relative tolerence was not being used correctly. Fixed this.

Arnie

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9