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