[system] / trunk / pg / lib / Value / AnswerChecker.pm Repository:
ViewVC logotype

View of /trunk/pg/lib/Value/AnswerChecker.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2626 - (download) (as text) (annotate)
Mon Aug 16 19:44:26 2004 UTC (15 years, 3 months ago) by dpvc
File size: 19577 byte(s)
One more fix for handling intervals properly (I think it's really
right this time).  Also, named constants that end in numbers will
produce TeX output with the number as a subscript (this was already
true for variable names).

    1 #############################################################
    2 #
    3 #  Implements the ->cmp method for Value objects.  This produces
    4 #  an answer checker appropriate for the type of object.
    5 #  Additional options can be passed to the checker to
    6 #  modify its action.
    7 #
    8 #  The individual Value packages are modified below to add the
    9 #  needed methods.
   10 #
   11 
   12 #############################################################
   13 
   14 package Value;
   15 
   16 #
   17 #  Create an answer checker for the given type of object
   18 #
   19 
   20 sub cmp_defaults {(
   21   showTypeWarnings => 1,
   22   showEqualErrors => 1,
   23 )}
   24 
   25 sub cmp {
   26   my $self = shift;
   27   $$Value::context->flags->set(StringifyAsTeX => 0);  # reset this, just in case.
   28   my $ans = new AnswerEvaluator;
   29   $ans->ans_hash(
   30     type => "Value (".$self->class.")",
   31     correct_ans => $self->string,
   32     correct_value => $self,
   33     $self->cmp_defaults,
   34     @_
   35   );
   36   $ans->install_evaluator(
   37     sub {
   38       my $ans = shift;
   39       #  can't seem to get $inputs_ref any other way
   40       $ans->{isPreview} = $self->getPG('$inputs_ref->{previewAnswers}');
   41       my $self = $ans->{correct_value};
   42       my $method = $ans->{cmp_check} || 'cmp_check';
   43       $ans->{cmp_class} = $self->cmp_class($ans) unless $ans->{cmp_class};
   44       $self->$method($ans);
   45     }
   46   );
   47   return $ans;
   48 }
   49 
   50 #
   51 #  Parse the student answer and compute its value,
   52 #    produce the preview strings, and then compare the
   53 #    student and professor's answers for equality.
   54 #
   55 sub cmp_check {
   56   my $self = shift; my $ans = shift;
   57   #
   58   #  Methods to call
   59   #
   60   my $cmp_equal = $ans->{cmp_equal} || 'cmp_equal';
   61   my $cmp_error = $ans->{cmp_error} || 'cmp_error';
   62   my $cmp_postprocess = $ans->{cmp_postprocess} || 'cmp_postprocess';
   63   #
   64   #  Parse and evaluate the student answer
   65   #
   66   $ans->score(0);  # assume failure
   67   $ans->{student_value} = $ans->{student_formula} = Parser::Formula($ans->{student_ans});
   68   $ans->{student_value} = Parser::Evaluate($ans->{student_formula})
   69     if defined($ans->{student_formula}) && $ans->{student_formula}->isConstant;
   70   #
   71   #  If it parsed OK, save the output forms and check if it is correct
   72   #   otherwise report an error
   73   #
   74   if (defined $ans->{student_value}) {
   75     $ans->{student_value} = Value::Formula->new($ans->{student_value})
   76        unless Value::isValue($ans->{student_value});
   77     $ans->{preview_latex_string} = $ans->{student_formula}->TeX;
   78     $ans->{preview_text_string}  = $ans->{student_formula}->string;
   79     $ans->{student_ans}          = $ans->{student_value}->stringify;
   80     $self->$cmp_equal($ans);
   81     $self->$cmp_postprocess($ans) if !$ans->{error_message};
   82   } else {
   83     $self->$cmp_error($ans);
   84   }
   85   return $ans;
   86 }
   87 
   88 #
   89 #  Check if the parsed student answer equals the professor's answer
   90 #
   91 sub cmp_equal {
   92   my $self = shift; my $ans = shift;
   93   if ($ans->{correct_value}->typeMatch($ans->{student_value},$ans)) {
   94     my $equal = eval {$ans->{correct_value} == $ans->{student_value}};
   95     if (defined($equal) || !$ans->{showEqualErrors}) {$ans->score(1) if $equal; return}
   96     my $cmp_error = $ans->{cmp_error} || 'cmp_error';
   97     $self->$cmp_error($ans);
   98   } else {
   99     $ans->{ans_message} = $ans->{error_message} =
  100       "Your answer isn't ".lc($ans->{cmp_class}).
  101         " (it looks like ".lc($ans->{student_value}->showClass).")"
  102      if !$ans->{isPreview} && $ans->{showTypeWarnings} && !$ans->{error_message};
  103   }
  104 }
  105 
  106 #
  107 #  Check if types are compatible for equality check
  108 #
  109 sub typeMatch {
  110   my $self = shift;  my $other = shift;
  111   return 1 unless ref($other);
  112   $self->type eq $other->type && $other->class ne 'Formula';
  113 }
  114 
  115 #
  116 #  Class name for cmp error messages
  117 #
  118 sub cmp_class {
  119   my $self = shift; my $ans = shift;
  120   my $class = $self->showClass; $class =~ s/Real //;
  121   return $class if $class =~ m/Formula/;
  122   return "an Interval or Union" if $class =~ m/Interval/i;
  123   return $class;
  124 }
  125 
  126 #
  127 #  Student answer evaluation failed.
  128 #  Report the error, with formatting, if possible.
  129 #
  130 sub cmp_error {
  131   my $self = shift; my $ans = shift;
  132   my $context = $$Value::context;
  133   my $message = $context->{error}{message};
  134   if ($context->{error}{pos}) {
  135     my $string = $context->{error}{string};
  136     my ($s,$e) = @{$context->{error}{pos}};
  137     $message =~ s/; see.*//;  # remove the position from the message
  138     $ans->{student_ans} =
  139        protectHTML(substr($string,0,$s)) .
  140        '<SPAN CLASS="parsehilight">' .
  141          protectHTML(substr($string,$s,$e-$s)) .
  142        '</SPAN>' .
  143        protectHTML(substr($string,$e));
  144   }
  145   $self->cmp_Error($ans,$message);
  146 }
  147 
  148 #
  149 #  Set the error message
  150 #
  151 sub cmp_Error {
  152   my $self = shift; my $ans = shift;
  153   return unless scalar(@_) > 0;
  154   $ans->score(0);
  155   $ans->{ans_message} = $ans->{error_message} = join("\n",@_);
  156 }
  157 
  158 #
  159 #  filled in by sub-classes
  160 #
  161 sub cmp_postprocess {}
  162 
  163 #
  164 #  Quote HTML characters
  165 #
  166 sub protectHTML {
  167     my $string = shift;
  168     $string =~ s/&/\&amp;/g;
  169     $string =~ s/</\&lt;/g;
  170     $string =~ s/>/\&gt;/g;
  171     $string;
  172 }
  173 
  174 #
  175 #  names for numbers
  176 #
  177 sub NameForNumber {
  178   my $self = shift; my $n = shift;
  179   my $name =  ('zeroth','first','second','third','fourth','fifth',
  180                'sixth','seventh','eighth','ninth','tenth')[$n];
  181   $name = "$n-th" if ($n > 10);
  182   return $name;
  183 }
  184 
  185 #
  186 #  Get a value from the safe compartment
  187 #
  188 sub getPG {
  189   my $self = shift;
  190   (WeBWorK::PG::Translator::PG_restricted_eval(shift))[0];
  191 }
  192 
  193 #############################################################
  194 #############################################################
  195 
  196 package Value::Real;
  197 
  198 sub cmp_defaults {(
  199   shift->SUPER::cmp_defaults,
  200   ignoreStrings => 1,
  201   ignoreInfinity => 1,
  202 )}
  203 
  204 sub typeMatch {
  205   my $self = shift; my $other = shift; my $ans = shift;
  206   return 1 unless ref($other);
  207   return 0 if $other->class eq 'Formula';
  208   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;
  214 }
  215 
  216 #############################################################
  217 
  218 package Value::Infinity;
  219 
  220 sub cmp_class {'a Number'};
  221 
  222 sub cmp_defaults {Value::Real->cmp_defaults};
  223 
  224 sub typeMatch {
  225   my $self = shift; my $other = shift; my $ans = shift;
  226   return 1 unless ref($other);
  227   return 0 if $other->class eq 'Formula';
  228   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;
  234 }
  235 
  236 #############################################################
  237 
  238 package Value::String;
  239 
  240 sub cmp_defaults {(
  241   Value::Real->cmp_defaults,
  242   typeMatch => 'Value::Real',
  243 )}
  244 
  245 sub cmp_class {
  246   my $self = shift; my $ans = shift; my $typeMatch = $ans->{typeMatch};
  247   return 'a Word' if !Value::isValue($typeMatch) || $typeMatch->class eq 'String';
  248   return $typeMatch->cmp_class;
  249 };
  250 
  251 sub typeMatch {
  252   my $self = shift; my $other = shift; my $ans = shift;
  253   return 0 if ref($other) && $other->class eq 'Formula';
  254   my $typeMatch = $ans->{typeMatch};
  255   return 1 if !Value::isValue($typeMatch) || $typeMatch->class eq 'String' ||
  256                  $self->type eq $other->type;
  257   return $typeMatch->typeMatch($other,$ans);
  258 }
  259 
  260 #############################################################
  261 
  262 package Value::Point;
  263 
  264 sub cmp_defaults {(
  265   shift->SUPER::cmp_defaults,
  266   showDimensionHints => 1,
  267   showCoordinateHints => 1,
  268 )}
  269 
  270 sub typeMatch {
  271   my $self = shift; my $other = shift; my $ans = shift;
  272   return ref($other) && $other->type eq 'Point' && $other->class ne 'Formula';
  273 }
  274 
  275 #
  276 #  Check for dimension mismatch and incorrect coordinates
  277 #
  278 sub cmp_postprocess {
  279   my $self = shift; my $ans = shift;
  280   return unless $ans->{score} == 0 && !$ans->{isPreview};
  281   if ($ans->{showDimensionHints} &&
  282       $self->length != $ans->{student_value}->length) {
  283     $self->cmp_Error($ans,"The dimension is incorrect"); return;
  284   }
  285   if ($ans->{showCoordinateHints}) {
  286     my @errors;
  287     foreach my $i (1..$self->length) {
  288       push(@errors,"The ".$self->NameForNumber($i)." coordinate is incorrect")
  289   if ($self->{data}[$i-1] != $ans->{student_value}{data}[$i-1]);
  290     }
  291     $self->cmp_Error($ans,@errors); return;
  292   }
  293 }
  294 
  295 #############################################################
  296 
  297 package Value::Vector;
  298 
  299 sub cmp_defaults {(
  300   shift->SUPER::cmp_defaults,
  301   showDimensionHints => 1,
  302   showCoordinateHints => 1,
  303   promotePoints => 0,
  304   parallel => 0,
  305   sameDirection => 0,
  306 )}
  307 
  308 sub typeMatch {
  309   my $self = shift; my $other = shift; my $ans = shift;
  310   return 0 unless ref($other) && $other->class ne 'Formula';
  311   $other = $ans->{student_value} = Value::Vector::promote($other)
  312     if $ans->{promotePoints} && $other->type eq 'Point';
  313   return $other->type eq 'Vector';
  314 }
  315 
  316 #
  317 #  check for dimension mismatch
  318 #        for parallel vectors, and
  319 #        for incorrect coordinates
  320 #
  321 sub cmp_postprocess {
  322   my $self = shift; my $ans = shift;
  323   return unless $ans->{score} == 0;
  324   if (!$ans->{isPreview} && $ans->{showDimensionHints} &&
  325       $self->length != $ans->{student_value}->length) {
  326     $self->cmp_Error($ans,"The dimension is incorrect"); return;
  327   }
  328  if ($ans->{parallel} &&
  329      $self->isParallel($ans->{student_value},$ans->{sameDirection})) {
  330    $ans->score(1); return;
  331  }
  332   if (!$ans->{isPreview} && $ans->{showCoordinateHints}) {
  333     my @errors;
  334     foreach my $i (1..$self->length) {
  335       push(@errors,"The ".$self->NameForNumber($i)." coordinate is incorrect")
  336   if ($self->{data}[$i-1] != $ans->{student_value}{data}[$i-1]);
  337     }
  338     $self->cmp_Error($ans,@errors); return;
  339   }
  340 }
  341 
  342 
  343 
  344 #############################################################
  345 
  346 package Value::Matrix;
  347 
  348 sub cmp_defaults {(
  349   shiftf->SUPER::cmp_defaults,
  350   showDimensionHints => 1,
  351   showEqualErrors => 0,
  352 )}
  353 
  354 sub typeMatch {
  355   my $self = shift; my $other = shift; my $ans = shift;
  356   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';
  360 }
  361 
  362 sub cmp_postprocess {
  363   my $self = shift; my $ans = shift;
  364   return unless $ans->{score} == 0 &&
  365     !$ans->{isPreview} && $ans->{showDimensionHints};
  366   my @d1 = $self->dimensions; my @d2 = $ans->{student_value}->dimensions;
  367   if (scalar(@d1) != scalar(@d2)) {
  368     $self->cmp_Error($ans,"Matrix dimension is not correct");
  369     return;
  370   } else {
  371     foreach my $i (0..scalar(@d1)-1) {
  372       if ($d1[$i] != $d2[$i]) {
  373   $self->cmp_Error($ans,"Matrix dimension is not correct");
  374   return;
  375       }
  376     }
  377   }
  378 }
  379 
  380 #############################################################
  381 
  382 package Value::Interval;
  383 
  384 sub cmp_defaults {(
  385   shift->SUPER::cmp_defaults,
  386   showEndpointHints => 1,
  387   showEndTypeHints => 1,
  388 )}
  389 
  390 sub typeMatch {
  391   my $self = shift; my $other = shift;
  392   return 0 unless ref($other) && $other->class ne 'Formula';
  393   return $other->length == 2 &&
  394          ($other->{open} eq '(' || $other->{open} eq '[') &&
  395          ($other->{close} eq ')' || $other->{close} eq ']')
  396      if $other->type =~ m/^(Point|List)$/;
  397   $other->type =~ m/^(Interval|Union)$/;
  398 }
  399 
  400 #
  401 #  Check for wrong enpoints and wrong type of endpoints
  402 #
  403 sub cmp_postprocess {
  404   my $self = shift; my $ans = shift;
  405   return unless $ans->{score} == 0 && !$ans->{isPreview};
  406   my $other = $ans->{student_value};
  407   return unless $other->class eq 'Interval';
  408   my @errors;
  409   if ($ans->{showEndpointHints}) {
  410     push(@errors,"Your left endpoint is incorrect")
  411       if ($self->{data}[0] != $other->{data}[0]);
  412     push(@errors,"Your right endpoint is incorrect")
  413       if ($self->{data}[1] != $other->{data}[1]);
  414   }
  415   if (scalar(@errors) == 0 && $ans->{showEndTypeHints}) {
  416     push(@errors,"The type of interval is incorrect")
  417       if ($self->{open}.$self->{close} ne $other->{open}.$other->{close});
  418   }
  419   $self->cmp_Error($ans,@errors);
  420 }
  421 
  422 #############################################################
  423 
  424 package Value::Union;
  425 
  426 sub typeMatch {
  427   my $self = shift; my $other = shift;
  428   return 0 unless ref($other) && $other->class ne 'Formula';
  429   return $other->length == 2 &&
  430          ($other->{open} eq '(' || $other->{open} eq '[') &&
  431          ($other->{close} eq ')' || $other->{close} eq ']')
  432      if $other->type =~ m/^(Point|List)$/;
  433   $other->type =~ m/^(Interval|Union)/;
  434 }
  435 
  436 #
  437 #  Use the List checker for unions, in order to get
  438 #  partial credit.  Set the various types for error
  439 #  messages.
  440 #
  441 sub cmp_defaults {(
  442   Value::List::cmp_defaults(@_),
  443   typeMatch => 'Value::Interval',
  444   list_type => 'union',
  445   entry_type => 'an interval',
  446 )}
  447 
  448 sub cmp_equal {Value::List::cmp_equal(@_)}
  449 
  450 #############################################################
  451 
  452 package Value::List;
  453 
  454 sub cmp_defaults {
  455   my $self = shift;
  456   return (
  457     Value::Real->cmp_defaults,
  458     showHints => undef,
  459     showLengthHints => undef,
  460 #    partialCredit => undef,
  461     partialCredit => 0,  #  only allow this once WW can deal with partial credit
  462     ordered => 0,
  463     entry_type => undef,
  464     list_type => lc($self->type),
  465     typeMatch => Value::makeValue($self->{data}[0]),
  466     allowParens => 0,
  467     showParens => 0,
  468    );
  469 }
  470 
  471 #
  472 #  Match anything but formulas
  473 #
  474 sub typeMatch {return !ref($other) || $other->class ne 'Formula'}
  475 
  476 #
  477 #  Handle removal of outermost parens in correct answer.
  478 #
  479 sub cmp {
  480   my $self = shift;
  481   my $cmp = $self->SUPER::cmp(@_);
  482   if (!$cmp->{rh_ans}{showParens}) {
  483     $self->{open} = $self->{close} = '';
  484     $cmp->ans_hash(correct_ans => $self->stringify);
  485   }
  486   return $cmp;
  487 }
  488 
  489 sub cmp_equal {
  490   my $self = shift; my $ans = shift;
  491   $ans->{showPartialCorrectAnswers} = $self->getPG('$showPartialCorrectAnswers');
  492 
  493   #
  494   #  get the paramaters
  495   #
  496   my $showTypeWarnings = $ans->{showTypeWarnings};
  497   my $showHints        = getOption($ans,'showHints');
  498   my $showLengthHints  = getOption($ans,'showLengthHints');
  499   my $partialCredit    = getOption($ans,'partialCredit');
  500   my $ordered = $ans->{ordered}; my $allowParens = $ans->{allowParens};
  501   my $typeMatch = $ans->{typeMatch};
  502   my $value     = $ans->{entry_type};
  503   my $ltype     = $ans->{list_type};
  504 
  505   $value = (Value::isValue($typeMatch)? lc($typeMatch->cmp_class): 'value')
  506     unless defined($value);
  507   $value =~ s/(real|complex) //; $ans->{cmp_class} = $value;
  508   $value =~ s/^an? //; $value = 'formula' if $value =~ m/formula/;
  509   $showTypeWarnings = $showHints = $showLengthHints = 0 if $ans->{isPreview};
  510 
  511   #
  512   #  Get the lists of correct and student answers
  513   #   (split formulas that return lists or unions)
  514   #
  515   my @correct = ();
  516   if ($self->class ne 'Formula') {@correct = $self->value}
  517     else {@correct = Value::List->splitFormula($self,$ans)}
  518   my $student = $ans->{student_value};
  519   my @student = ($student);
  520   if ($student->class eq 'Formula' && $student->type eq $self->type) {
  521     @student = Value::List->splitFormula($student,$ans);
  522   } elsif ($student->class ne 'Formula' && $student->class eq $self->class &&
  523       ($allowParens || (!$student->{open} && !$student->{close}))) {
  524     @student = @{$student->{data}};
  525   }
  526   return if $ans->{split_error};
  527   if (scalar(@correct) == 0 && scalar(@student) == 0) {$ans->score(1); return}
  528 
  529   #
  530   #  Initialize the score
  531   #
  532   my $M = scalar(@correct);
  533   my $m = scalar(@student);
  534   my $maxscore = ($m > $M)? $m : $M;
  535   my $score = 0; my @errors; my $i = 0;
  536 
  537   #
  538   #  Loop through student answers looking for correct ones
  539   #
  540   ENTRY: foreach my $entry (@student) {
  541     $i++;
  542     $entry = Value::makeValue($entry);
  543     $entry = Value::Formula->new($entry) if !Value::isValue($entry);
  544     if ($ordered) {
  545       if (eval {shift(@correct) == $entry}) {$score++; next ENTRY}
  546     } else {
  547       foreach my $k (0..$#correct) {
  548   if (eval {$correct[$k] == $entry}) {
  549     splice(@correct,$k,1);
  550     $score++; next ENTRY;
  551   }
  552       }
  553     }
  554     #
  555     #  Give messages about incorrect answers
  556     #
  557     my $nth = ''; my $answer = 'answer'; my $class = $self->cmp_class;
  558     if (scalar(@student) > 1) {
  559       $nth = ' '.$self->NameForNumber($i);
  560       $class = $ans->{cmp_class};
  561       $answer = 'value';
  562     }
  563     if ($showTypeWarnings && !$typeMatch->typeMatch($entry,$ans) &&
  564   !($ans->{ignoreStrings} && $entry->class eq 'String')) {
  565       push(@errors,"Your$nth $answer isn't ".lc($class).
  566      " (it looks like ".lc($entry->showClass).")");
  567     } elsif ($showHints && $m > 1) {
  568       push(@errors,"Your$nth $value is incorrect");
  569     }
  570   }
  571 
  572   #
  573   #  Give hints about extra or missing answsers
  574   #
  575   if ($showLengthHints) {
  576     $value =~ s/ or /s or /; # fix "interval or union"
  577     push(@errors,"There should be more ${value}s in your $ltype")
  578       if ($score == $m && scalar(@correct) > 0);
  579     push(@errors,"There should be fewer ${value}s in your $ltype")
  580       if ($score < $maxscore && $score == $M);
  581   }
  582 
  583   #
  584   #  Finalize the score
  585   #
  586   $score = 0 if ($score != $maxscore && !$partialCredit);
  587   $ans->score($score/$maxscore);
  588   push(@errors,"Score = $ans->{score}") if $ans->{debug};
  589   $ans->{error_message} = $ans->{ans_message} = join("\n",@errors);
  590 }
  591 
  592 #
  593 #  Split a formula that is a list or union into a
  594 #    list of formulas (or Value objects).
  595 #
  596 sub splitFormula {
  597   my $self = shift; my $formula = shift; my $ans = shift;
  598   my @formula; my @entries;
  599   if ($formula->type eq 'List') {@entries = @{$formula->{tree}{coords}}}
  600       else {@entries = $formula->{tree}->makeUnion}
  601   foreach my $entry (@entries) {
  602     my $v = Parser::Formula($entry);
  603        $v = Parser::Evaluate($v) if (defined($v) && $v->isConstant);
  604     push(@formula,$v);
  605     #
  606     #  There shouldn't be an error evaluating the formula,
  607     #    but you never know...
  608     #
  609     if (!defined($v)) {
  610       $ans->{split_error} = 1;
  611       my $cmp_error = $ans->{cmp_error} || 'cmp_error';
  612       $self->$cmp_error; return;
  613     }
  614   }
  615   return @formula;
  616 }
  617 
  618 #
  619 #  Return the value if it is defined, otherwise use a default
  620 #
  621 sub getOption {
  622   my $ans = shift; my $name = shift;
  623   my $value = $ans->{$name};
  624   return $value if defined($value);
  625   return $ans->{showPartialCorrectAnswers};
  626 }
  627 
  628 #############################################################
  629 
  630 package Value::Formula;
  631 
  632 sub cmp_defaults {
  633   my $self = shift;
  634 
  635   return (
  636     Value::Union::cmp_defaults($self,@_),
  637     typeMatch => Value::Formula->new("(1,2]"),
  638   ) if $self->type eq 'Union';
  639 
  640   return Value::Real::cmp_defaults($self,@_) unless $self->type eq 'List';
  641 
  642   return (
  643     Value::List::cmp_defaults($self,@_),
  644     typeMatch => Value::Formula->new(($self->createRandomPoints(1))[1]->[0]{data}[0]),
  645   );
  646 }
  647 
  648 #
  649 #  Get the types from the values of the formulas
  650 #     and compare those.
  651 #
  652 sub typeMatch {
  653   my $self = shift; my $other = shift; my $ans = shift;
  654   return 1 if $self->type eq $other->type;
  655   my $typeMatch = ($self->createRandomPoints(1))[1]->[0];
  656   $other = eval {($other->createRandomPoints(1))[1]->[0]} if ($other->class eq 'Formula');
  657   return 1 unless defined($other); # can't really tell, so don't report type mismatch
  658   $typeMatch->typeMatch($other,$ans);
  659 }
  660 
  661 sub cmp_equal {
  662   my $self = shift; my $ans = shift;
  663   #
  664   #  Get the problem's seed
  665   #
  666   $self->{context}->flags->set(
  667     random_seed => $self->getPG('$PG_original_problemSeed')
  668   );
  669 
  670   #
  671   #  Use the list checker if the formula is a list or union
  672   #    Otherwise use the normal checker
  673   #
  674   if ($self->type =~ m/^(List|Union)$/) {
  675     Value::List::cmp_equal($self,$ans);
  676   } else {
  677     $self->SUPER::cmp_equal($ans);
  678   }
  679 }
  680 
  681 #
  682 #  Replace the ones in Value::Formula
  683 #
  684 sub PGseedRandom {
  685   my $self = shift;
  686   return if $self->{PGrandom};
  687   $self->{PGrandom} = new PGrandom($self->{context}->flag('random_seed'));
  688 }
  689 sub PGgetRandom {shift->{PGrandom}->getRandom(@_)}
  690 
  691 #############################################################
  692 
  693 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9