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