[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 1088 - (download) (as text) (annotate)
Mon Jun 9 21:05:11 2003 UTC (16 years, 8 months ago) by gage
File size: 48577 byte(s)
Commented out code  that is not used (I believe)

comp_cplx4 and cplx_cmp4

-- Mike

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9