| … | |
… | |
| 17 | # Create an answer checker for the given type of object |
17 | # Create an answer checker for the given type of object |
| 18 | # |
18 | # |
| 19 | |
19 | |
| 20 | sub cmp_defaults {( |
20 | sub cmp_defaults {( |
| 21 | showTypeWarnings => 1, |
21 | showTypeWarnings => 1, |
| 22 | showEqualErrors => 1, |
22 | showEqualErrors => 1, |
|
|
23 | ignoreStrings => 1, |
| 23 | )} |
24 | )} |
| 24 | |
25 | |
| 25 | sub cmp { |
26 | sub cmp { |
| 26 | my $self = shift; |
27 | my $self = shift; |
| 27 | $$Value::context->flags->set(StringifyAsTeX => 0); # reset this, just in case. |
28 | $$Value::context->flags->set(StringifyAsTeX => 0); # reset this, just in case. |
| … | |
… | |
| 88 | # |
89 | # |
| 89 | # Check if the parsed student answer equals the professor's answer |
90 | # Check if the parsed student answer equals the professor's answer |
| 90 | # |
91 | # |
| 91 | sub cmp_equal { |
92 | sub cmp_equal { |
| 92 | my $self = shift; my $ans = shift; |
93 | my $self = shift; my $ans = shift; |
|
|
94 | my $correct = $ans->{correct_value}; |
|
|
95 | my $student = $ans->{student_value}; |
| 93 | if ($ans->{correct_value}->typeMatch($ans->{student_value},$ans)) { |
96 | if ($correct->typeMatch($student,$ans)) { |
| 94 | my $equal = eval {$ans->{correct_value} == $ans->{student_value}}; |
97 | my $equal = eval {$correct == $student}; |
| 95 | if (defined($equal) || !$ans->{showEqualErrors}) {$ans->score(1) if $equal; return} |
98 | if (defined($equal) || !$ans->{showEqualErrors}) {$ans->score(1) if $equal; return} |
| 96 | my $cmp_error = $ans->{cmp_error} || 'cmp_error'; |
99 | my $cmp_error = $ans->{cmp_error} || 'cmp_error'; |
| 97 | $self->$cmp_error($ans); |
100 | $self->$cmp_error($ans); |
| 98 | } else { |
101 | } else { |
|
|
102 | return if $ans->{ignoreStrings} && (!Value::isValue($student) || $student->type eq 'String'); |
| 99 | $ans->{ans_message} = $ans->{error_message} = |
103 | $ans->{ans_message} = $ans->{error_message} = |
| 100 | "Your answer isn't ".lc($ans->{cmp_class}). |
104 | "Your answer isn't ".lc($ans->{cmp_class}). |
| 101 | " (it looks like ".lc($ans->{student_value}->showClass).")" |
105 | " (it looks like ".lc($student->showClass).")" |
| 102 | if !$ans->{isPreview} && $ans->{showTypeWarnings} && !$ans->{error_message}; |
106 | if !$ans->{isPreview} && $ans->{showTypeWarnings} && !$ans->{error_message}; |
| 103 | } |
107 | } |
| 104 | } |
108 | } |
| 105 | |
109 | |
| 106 | # |
110 | # |
| … | |
… | |
| 195 | |
199 | |
| 196 | package Value::Real; |
200 | package Value::Real; |
| 197 | |
201 | |
| 198 | sub cmp_defaults {( |
202 | sub cmp_defaults {( |
| 199 | shift->SUPER::cmp_defaults, |
203 | shift->SUPER::cmp_defaults, |
| 200 | ignoreStrings => 1, |
|
|
| 201 | ignoreInfinity => 1, |
204 | ignoreInfinity => 1, |
| 202 | )} |
205 | )} |
| 203 | |
206 | |
| 204 | sub typeMatch { |
207 | sub typeMatch { |
| 205 | my $self = shift; my $other = shift; my $ans = shift; |
208 | my $self = shift; my $other = shift; my $ans = shift; |
| 206 | return 1 unless ref($other); |
209 | return 1 unless ref($other); |
| 207 | return 0 if $other->class eq 'Formula'; |
210 | return 0 if $other->class eq 'Formula'; |
| 208 | return 1 if $other->type eq 'Infinity' && $ans->{ignoreInfinity}; |
211 | return 1 if $other->type eq 'Infinity' && $ans->{ignoreInfinity}; |
| 209 | if ($other->type eq 'String' && $ans->{ignoreStrings}) { |
|
|
| 210 | $ans->{showEqualErrors} = 0; |
|
|
| 211 | return 1; |
|
|
| 212 | } |
|
|
| 213 | $self->type eq $other->type; |
212 | $self->type eq $other->type; |
| 214 | } |
213 | } |
| 215 | |
214 | |
| 216 | ############################################################# |
215 | ############################################################# |
| 217 | |
216 | |
| … | |
… | |
| 224 | sub typeMatch { |
223 | sub typeMatch { |
| 225 | my $self = shift; my $other = shift; my $ans = shift; |
224 | my $self = shift; my $other = shift; my $ans = shift; |
| 226 | return 1 unless ref($other); |
225 | return 1 unless ref($other); |
| 227 | return 0 if $other->class eq 'Formula'; |
226 | return 0 if $other->class eq 'Formula'; |
| 228 | return 1 if $other->type eq 'Number'; |
227 | return 1 if $other->type eq 'Number'; |
| 229 | if ($other->type eq 'String' && $ans->{ignoreStrings}) { |
|
|
| 230 | $ans->{showEqualErrors} = 0; |
|
|
| 231 | return 1; |
|
|
| 232 | } |
|
|
| 233 | $self->type eq $other->type; |
228 | $self->type eq $other->type; |
| 234 | } |
229 | } |
| 235 | |
230 | |
| 236 | ############################################################# |
231 | ############################################################# |
| 237 | |
232 | |
| … | |
… | |
| 306 | )} |
301 | )} |
| 307 | |
302 | |
| 308 | sub typeMatch { |
303 | sub typeMatch { |
| 309 | my $self = shift; my $other = shift; my $ans = shift; |
304 | my $self = shift; my $other = shift; my $ans = shift; |
| 310 | return 0 unless ref($other) && $other->class ne 'Formula'; |
305 | return 0 unless ref($other) && $other->class ne 'Formula'; |
| 311 | $other = $ans->{student_value} = Value::Vector::promote($other) |
306 | return $other->type eq 'Vector' || |
| 312 | if $ans->{promotePoints} && $other->type eq 'Point'; |
307 | ($ans->{promotePoints} && $other->type eq 'Point'); |
| 313 | return $other->type eq 'Vector'; |
|
|
| 314 | } |
308 | } |
| 315 | |
309 | |
| 316 | # |
310 | # |
| 317 | # check for dimension mismatch |
311 | # check for dimension mismatch |
| 318 | # for parallel vectors, and |
312 | # for parallel vectors, and |
| … | |
… | |
| 352 | )} |
346 | )} |
| 353 | |
347 | |
| 354 | sub typeMatch { |
348 | sub typeMatch { |
| 355 | my $self = shift; my $other = shift; my $ans = shift; |
349 | my $self = shift; my $other = shift; my $ans = shift; |
| 356 | return 0 unless ref($other) && $other->class ne 'Formula'; |
350 | return 0 unless ref($other) && $other->class ne 'Formula'; |
| 357 | $other = $ans->{student_value} = $self->make($other->{data}) |
|
|
| 358 | if $other->class eq 'Point'; |
|
|
| 359 | return $other->type eq 'Matrix'; |
351 | return $other->type eq 'Matrix' || |
|
|
352 | ($other->type =~ m/^(Point|list)$/ && |
|
|
353 | $other->{open}.$other->{close} eq $self->{open}.$self->{close}); |
| 360 | } |
354 | } |
| 361 | |
355 | |
| 362 | sub cmp_postprocess { |
356 | sub cmp_postprocess { |
| 363 | my $self = shift; my $ans = shift; |
357 | my $self = shift; my $ans = shift; |
| 364 | return unless $ans->{score} == 0 && |
358 | return unless $ans->{score} == 0 && |