[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 2627 - (download) (as text) (annotate)
Mon Aug 16 20:22:16 2004 UTC (15 years, 3 months ago) by dpvc
File size: 19426 byte(s)
Removed unwanted side-effects of some type-match checks, and moved the
ignoreStrings flag so that it is available for the checkers of ANY type.

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9