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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1072 - (view) (download) (as text)

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9