[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 3319 - (view) (download) (as text)

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9