[system] / trunk / pg / lib / Parser / Legacy / PGcomplexmacros.pl Repository:
ViewVC logotype

View of /trunk/pg/lib/Parser/Legacy/PGcomplexmacros.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 4928 - (download) (as text) (annotate)
Tue Apr 17 00:36:16 2007 UTC (12 years, 7 months ago) by dpvc
File size: 51832 byte(s)
Modified the way the Parser-based versions of the traditional answer
checkers get copies of their contexts.  They now use a new method
getCopy to obtain the copy either from the problem's context table or
the default table (rather than only from the default).  That way the
instructor can use parserCustomization.pl to customize the contexts
used by the answer checkers.

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9