You can subscribe to this list here.
2004 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
(58) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2005 |
Jan
(53) |
Feb
(56) |
Mar
|
Apr
|
May
(30) |
Jun
(78) |
Jul
(121) |
Aug
(155) |
Sep
(77) |
Oct
(61) |
Nov
(45) |
Dec
(94) |
2006 |
Jan
(116) |
Feb
(33) |
Mar
(11) |
Apr
(23) |
May
(60) |
Jun
(89) |
Jul
(130) |
Aug
(109) |
Sep
(124) |
Oct
(63) |
Nov
(82) |
Dec
(45) |
2007 |
Jan
(31) |
Feb
(35) |
Mar
(123) |
Apr
(36) |
May
(18) |
Jun
(134) |
Jul
(133) |
Aug
(241) |
Sep
(126) |
Oct
(31) |
Nov
(15) |
Dec
(5) |
2008 |
Jan
(11) |
Feb
(6) |
Mar
(16) |
Apr
(29) |
May
(43) |
Jun
(149) |
Jul
(27) |
Aug
(29) |
Sep
(37) |
Oct
(20) |
Nov
(4) |
Dec
(6) |
2009 |
Jan
(34) |
Feb
(30) |
Mar
(16) |
Apr
(6) |
May
(1) |
Jun
(32) |
Jul
(22) |
Aug
(7) |
Sep
(18) |
Oct
(50) |
Nov
(22) |
Dec
(8) |
2010 |
Jan
(17) |
Feb
(15) |
Mar
(10) |
Apr
(9) |
May
(67) |
Jun
(30) |
Jul
|
Aug
|
Sep
(2) |
Oct
|
Nov
(1) |
Dec
|
From: Mike G. v. a. <we...@ma...> - 2005-07-07 02:44:02
|
Log Message: ----------- Added some pod documentation to this file. It needs more -- several methods of MatrixReal1 have been overridden Modified Files: -------------- pg/lib: Matrix.pm Revision Data ------------- Index: Matrix.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Matrix.pm,v retrieving revision 1.7 retrieving revision 1.8 diff -Llib/Matrix.pm -Llib/Matrix.pm -u -r1.7 -r1.8 --- lib/Matrix.pm +++ lib/Matrix.pm @@ -1,3 +1,21 @@ +=head1 NAME + +Matrix - Matrix of Reals + +Implements overrides for MatrixReal.pm for WeBWorK + +=head1 DESCRIPTION + + + +=head1 SYNOPSIS + + +=head3 Matrix Methods: + +=cut + + BEGIN { be_strict(); # an alias for use strict. This means that all global variable must contain main:: as a prefix. |
From: dpvc v. a. <we...@ma...> - 2005-07-06 15:41:18
|
Log Message: ----------- Make tth use unicode for the preview (as it does for the calls within the body of the problem). Also, do the preview in display mode, but fix the tables so that they won't have unwanted borders (really need to fix the ur.css to get this right) and remove the unneeded initial <BR> that tth produces. Modified Files: -------------- webwork-modperl/lib/WeBWorK/ContentGenerator: Problem.pm Revision Data ------------- Index: Problem.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Problem.pm,v retrieving revision 1.174 retrieving revision 1.175 diff -Llib/WeBWorK/ContentGenerator/Problem.pm -Llib/WeBWorK/ContentGenerator/Problem.pm -u -r1.174 -r1.175 --- lib/WeBWorK/ContentGenerator/Problem.pm +++ lib/WeBWorK/ContentGenerator/Problem.pm @@ -319,8 +319,8 @@ # construct TTH command line my $tthCommand = $ce->{externalPrograms}->{tth} - . " -L -f5 -r 2> /dev/null <<END_OF_INPUT; echo > /dev/null\n" - . $tthPreamble . "\\(" . $tex . "\\)\n" + . " -L -f5 -u -r 2> /dev/null <<END_OF_INPUT; echo > /dev/null\n" + . $tthPreamble . "\\[" . $tex . "\\]\n" . "END_OF_INPUT\n"; # call tth @@ -328,6 +328,9 @@ if ($?) { return "<b>[tth failed: $? $@]</b>"; } else { + # avoid border problems in tables and remove unneeded initial <br> + $result =~ s/(<table [^>]*)>/$1 CLASS="ArrayLayout">/gi; + $result =~ s!\s*<br clear="all" />!!; return $result; } |
From: Mike G. v. a. <we...@ma...> - 2005-07-05 22:16:21
|
Log Message: ----------- Added some pod documentation Modified Files: -------------- pg/lib: Matrix.pm Revision Data ------------- Index: Matrix.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Matrix.pm,v retrieving revision 1.6 retrieving revision 1.7 diff -Llib/Matrix.pm -Llib/Matrix.pm -u -r1.6 -r1.7 --- lib/Matrix.pm +++ lib/Matrix.pm @@ -10,6 +10,15 @@ $Matrix::DEFAULT_FORMAT = '% #-19.12E '; # allows specification of the format + +=head4 + + Method $matrix->_stringify() + -- overrides MatrixReal1 display mode + +=cut + + sub _stringify { my($object,$argument,$flag) = @_; @@ -33,6 +42,12 @@ return($s); } +=head4 + + Method $matrix->rh_options + +=cut + sub rh_options { my $self = shift; my $last_element = $#$self; @@ -40,6 +55,13 @@ $self->[$last_element]; # provides a reference to the options hash MEG } +=head4 + + Method $matrix->trace + + Returns: scalar which is the trace of the matrix. + +=cut sub trace { my $self = shift; @@ -52,6 +74,13 @@ } $sum; } + +=head4 + + Method $matrix->new_from_array_ref + +=cut + sub new_from_array_ref { # this will build a matrix or a row vector from [a, b, c, ] my $class = shift; my $array = shift; @@ -62,11 +91,23 @@ $matrix; } +=head4 + + Method $matrix->array_ref + +=cut + sub array_ref { my $this = shift; $this->[0]; } +=head4 + + Method $matrix->list + +=cut + sub list { # this is used only for column vectors my $self = shift; my @list = (); @@ -77,6 +118,13 @@ } @list; } + +=head4 + + Method $matrix->new_from_list + +=cut + sub new_from_list { # this builds a row vector from an array my $class = shift; my @list = @_; @@ -90,6 +138,13 @@ } $matrix; } + +=head4 + + Method $matrix->new_row_matrix + +=cut + sub new_row_matrix { # this builds a row vector from an array my $class = shift; my @list = @_; @@ -103,11 +158,25 @@ } $matrix; } + +=head4 + + Method $matrix->proj + +=cut + sub proj{ my $self = shift; my ($vec) = @_; $self * $self ->proj_coeff($vec); } + +=head4 + + Method $matrix->proj_coeff + +=cut + sub proj_coeff{ my $self= shift; my ($vec) = @_; @@ -120,6 +189,13 @@ warn "A unique adapted answer could not be determined. Possibly the parameters have coefficient zero.<br> dim = $dim base_matrix is $base_matrix\n" if $dim; # only print if the dim is not zero. $x_vector; } + +=head4 + + Method $matrix->new_column_matrix + +=cut + sub new_column_matrix { my $class = shift; my $vec = shift; @@ -132,11 +208,14 @@ } $matrix; } + =head4 This method takes an array of column vectors, or an array of arrays, and converts them to a matrix where each column is one of the previous vectors. + + Method $matrix->new_from_col_vecs =cut @@ -179,12 +258,31 @@ # Modifications to MatrixReal.pm which allow use of complex entries ###################################################################### +=head3 + + Overrides of MatrixReal which allow use of complex entries + +=cut + +=head4 + + Method $matrix->new_from_col_vecs + +=cut + sub cp { # MEG makes new copies of complex number my $z = shift; return $z unless ref($z); my $w = Complex1::cplx($z->Re,$z->Im); return $w; } + +=head4 + + Method $matrix->copy + +=cut + sub copy { croak "Usage: \$matrix1->copy(\$matrix2);" @@ -221,11 +319,25 @@ ################################################################### # MEG added 6/25/03 to accomodate complex entries + +=head4 + + Method $matrix->conj + +=cut + sub conj { my $elem = shift; $elem = (ref($elem)) ? ($elem->conjugate) : $elem; $elem; } + +=head4 + + Method $matrix->transpose + +=cut + sub transpose { croak "Usage: \$matrix1->transpose(\$matrix2);" @@ -269,7 +381,11 @@ $matrix1; } +=head4 + Method $matrix->decompose_LR + +=cut sub decompose_LR { @@ -285,7 +401,7 @@ my($sign) = 1; my($swap); my($temp); -# Why won't this work on non-square matrices? +# FIXEME Why won't this work on non-square matrices? # croak "MatrixReal1::decompose_LR(): matrix is not quadratic" # unless ($rows == $cols); croak "MatrixReal1::decompose_LR(): matrix has more rows than columns" |
From: Sam H. v. a. <we...@ma...> - 2005-07-05 22:01:56
|
Log Message: ----------- chech tthPreamble path and mtime to deal with changes to the file=20 between runs. this is necessary now that dangerousMacros.pl is cached.=20 Closes bug #798. Modified Files: -------------- pg/macros: dangerousMacros.pl Revision Data ------------- Index: dangerousMacros.pl =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D RCS file: /webwork/cvs/system/pg/macros/dangerousMacros.pl,v retrieving revision 1.34 retrieving revision 1.35 diff -Lmacros/dangerousMacros.pl -Lmacros/dangerousMacros.pl -u -r1.34 -r= 1.35 --- macros/dangerousMacros.pl +++ macros/dangerousMacros.pl @@ -436,21 +436,35 @@ # Global macros: # None =20 -my ($tthPreambleFile, $tthPreambleContents); # the contents of this file= will not change during problem compilation - # it only needs to be read = once +# the contents of this file will not change during problem compilation i= t +# only needs to be read once. however, the contents of the file may chan= ge, +# and indeed the file refered to may change, between rendering passes. t= hus, +# we need to keep track of the file name and the mtime as well. +my ($tthPreambleFile, $tthPreambleMtime, $tthPreambleContents); + sub tth { my $inputString =3D shift; - - # read the contents of the tthPreamble.tex file, unless it has already = been read - unless ( defined( $tthPreambleContents) ) { - $tthPreambleFile =3D "${templateDirectory}tthPreamble.tex" if ( -r "${= templateDirectory}tthPreamble.tex" ); - if ( defined($tthPreambleFile) ) { +=09 + my $thisFile =3D "${templateDirectory}tthPreamble.tex" if -r "${templat= eDirectory}tthPreamble.tex"; +=09 + if (defined $thisFile) { + my $thisMtime =3D (stat $thisFile)[9]; + my $load =3D=20 + # load preamble if we haven't loaded it ever + (not defined $tthPreambleFile or not defined $tthPreambleMtime or not= defined $tthPreambleContents) + || + # load if the file path has changed + ($tthPreambleFile ne $thisFile) + || + # load if the file has been modified + ($tthPreambleMtime < $thisMtime); + =09 + if ($load) { local(*TTHIN); open (TTHIN, "${templateDirectory}tthPreamble.tex") || die "Can't ope= n file ${templateDirectory}tthPreamble.tex"; - #my @tthPreambleArray =3D <TTHIN>; local($/); $/ =3D undef; - $tthPreambleContents =3D <TTHIN>;#join("",@tthPreambleArray); + $tthPreambleContents =3D <TTHIN>; close(TTHIN); =20 $tthPreambleContents =3D~ s/(.)\n/$1%\n/g; # thanks to Jim Martino @@ -459,12 +473,11 @@ # adding supurious paragrap= hs to output. =20 $tthPreambleContents .=3D"%\n"; # solves the problem if t= he file doesn't end with a return. - - } else { - $tthPreambleContents =3D ""; } + } else { + $tthPreambleContents =3D ""; } - +=09 $inputString =3D $tthPreambleContents . $inputString; $inputString =3D "<<END_OF_TTH_INPUT_STRING;\n\n\n" . $inputStrin= g . "\nEND_OF_TTH_INPUT_STRING\necho \"\" >/dev/null"; #it's not clear wh= y another command is needed. =20 @@ -496,25 +509,25 @@ $string =3D~ s/\x5C/\\/g; #\ 92 \ $string =3D~ s/\x7B/\{/g; #{ 123 {= ; $string =3D~ s/\x7D/\}/g; #} 125 }= ; - $string =3D~ s/\xE7/\Á/g; #=E7 231 = 93; - $string =3D~ s/\xE6/\Ê/g; #=E6 230 = 02; - $string =3D~ s/\xE8/\Ë/g; #=E8 232 = 03; - $string =3D~ s/\xF3/\Û/g; #=F3 243 = 19; - $string =3D~ s/\xA5/\•/g; #=A5 165 &bu= ll; - $string =3D~ s/\xB2/\≤/g; #=B2 178 ≤ - $string =3D~ s/\xB3/\≥/g; #=B3 179 ≥ - $string =3D~ s/\xB6/\∂/g; #=B6 182 &pa= rt; - $string =3D~ s/\xCE/\Œ/g; #=CE 206 = 38; - $string =3D~ s/\xD6/\˜/g; #=D6 214 = 32; - $string =3D~ s/\xD9/\Ÿ/g; #=D9 217 &Yu= ml; - $string =3D~ s/\xDA/\⁄/g; #=DA 218 &f= rasl; - $string =3D~ s/\xF5/\ı/g; #=F5 245 = 05 - $string =3D~ s/\xF6/\ˆ/g; #=F6 246 = 10; - $string =3D~ s/\xF7/\Á/g; #=F7 247 = 93; - $string =3D~ s/\xF8/\¯/g; #=F8 248 = 75; - $string =3D~ s/\xF9/\˘/g; #=F9 249 = 28; - $string =3D~ s/\xFA/\˙/g; #=FA 250 = 29; - $string =3D~ s/\xFB/\˚;/g; #=FB 251 &#= 730; + $string =3D~ s/\xE7/\Á/g; #=C1 231 = 93; + $string =3D~ s/\xE6/\Ê/g; #=CA 230 = 02; + $string =3D~ s/\xE8/\Ë/g; #=CB 232 = 03; + $string =3D~ s/\xF3/\Û/g; #=DB 243 = 19; + $string =3D~ s/\xA5/\•/g; #=80 165 &bu= ll; + $string =3D~ s/\xB2/\≤/g; #=BE 178 ≤ + $string =3D~ s/\xB3/\≥/g; #=84 179 ≥ + $string =3D~ s/\xB6/\∂/g; #=8F 182 &pa= rt; + $string =3D~ s/\xCE/\Œ/g; #=91 206 = 38; + $string =3D~ s/\xD6/\˜/g; #=F7 214 = 32; + $string =3D~ s/\xD9/\Ÿ/g; #=8D 217 &Yu= ml; + $string =3D~ s/\xDA/\⁄/g; #=8E 218 &f= rasl; + $string =3D~ s/\xF5/\ı/g; #=9E 245 = 05 + $string =3D~ s/\xF6/\ˆ/g; #=96 246 = 10; + $string =3D~ s/\xF7/\Á/g; #=97 247 = 93; + $string =3D~ s/\xF8/\¯/g; #=AF 248 = 75; + $string =3D~ s/\xF9/\˘/g; #=98 249 = 28; + $string =3D~ s/\xFA/\˙/g; #=99 250 = 29; + $string =3D~ s/\xFB/\˚;/g; #=9A 251 &#= 730; $string; } =20 |
From: dpvc v. a. <we...@ma...> - 2005-07-05 20:52:22
|
Log Message: ----------- A couple of other problem set -> homework set changes. Modified Files: -------------- webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor: Assigner.pm Stats.pm StudentProgress.pm Revision Data ------------- Index: Stats.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/Stats.pm,v retrieving revision 1.53 retrieving revision 1.54 diff -Llib/WeBWorK/ContentGenerator/Instructor/Stats.pm -Llib/WeBWorK/ContentGenerator/Instructor/Stats.pm -u -r1.53 -r1.54 --- lib/WeBWorK/ContentGenerator/Instructor/Stats.pm +++ lib/WeBWorK/ContentGenerator/Instructor/Stats.pm @@ -20,7 +20,7 @@ =head1 NAME WeBWorK::ContentGenerator::Instructor::Stats - Display statistics by user or -problem set. +homework set. =cut Index: Assigner.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/Assigner.pm,v retrieving revision 1.28 retrieving revision 1.29 diff -Llib/WeBWorK/ContentGenerator/Instructor/Assigner.pm -Llib/WeBWorK/ContentGenerator/Instructor/Assigner.pm -u -r1.28 -r1.29 --- lib/WeBWorK/ContentGenerator/Instructor/Assigner.pm +++ lib/WeBWorK/ContentGenerator/Instructor/Assigner.pm @@ -19,7 +19,7 @@ =head1 NAME -WeBWorK::ContentGenerator::Instructor::Assigner - Assign problem sets to users. +WeBWorK::ContentGenerator::Instructor::Assigner - Assign homework sets to users. =cut Index: StudentProgress.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/StudentProgress.pm,v retrieving revision 1.15 retrieving revision 1.16 diff -Llib/WeBWorK/ContentGenerator/Instructor/StudentProgress.pm -Llib/WeBWorK/ContentGenerator/Instructor/StudentProgress.pm -u -r1.15 -r1.16 --- lib/WeBWorK/ContentGenerator/Instructor/StudentProgress.pm +++ lib/WeBWorK/ContentGenerator/Instructor/StudentProgress.pm @@ -523,7 +523,7 @@ 'or 0 if there are no attempts.' ), CGI::br(), - "Click on student's name to see the student's version of the problem set. + "Click on student's name to see the student's version of the homework set. Click heading to sort table. ", CGI::br(), CGI::br(), |
From: Sam H. v. a. <we...@ma...> - 2005-07-05 18:59:46
|
Log Message: ----------- renamed "problem set" to "homework set" in page content. closes bug #797. Modified Files: -------------- webwork2/lib/WeBWorK/ContentGenerator: Feedback.pm GatewayQuiz.pm Hardcopy.pm Problem.pm ProblemSet.pm webwork2/lib/WeBWorK/ContentGenerator/Instructor: Assigner.pm Index.pm ProblemList.pm ProblemSetEditor.pm ProblemSetList.pm SetMaker.pm SetsAssignedToUser.pm UsersAssignedToSet.pm Revision Data ------------- Index: Problem.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/Problem.pm,v retrieving revision 1.173 retrieving revision 1.174 diff -Llib/WeBWorK/ContentGenerator/Problem.pm -Llib/WeBWorK/ContentGenerator/Problem.pm -u -r1.173 -r1.174 --- lib/WeBWorK/ContentGenerator/Problem.pm +++ lib/WeBWorK/ContentGenerator/Problem.pm @@ -740,7 +740,7 @@ if ($self->{invalidSet}) { return CGI::div({class=>"ResultsWithError"}, - CGI::p("The selected problem set (" . $urlpath->arg("setID") . ") is not a valid set for " . $r->param("effectiveUser") . ".")); + CGI::p("The selected homework set (" . $urlpath->arg("setID") . ") is not a valid set for " . $r->param("effectiveUser") . ".")); } if ($self->{invalidProblem}) { @@ -750,7 +750,7 @@ unless ($self->{isOpen}) { return CGI::div({class=>"ResultsWithError"}, - CGI::p("This problem is not available because the problem set that contains it is not yet open.")); + CGI::p("This problem is not available because the homework set that contains it is not yet open.")); } # unpack some useful variables my $set = $self->{set}; @@ -854,7 +854,7 @@ ); } else { if (before($set->open_date) or after($set->due_date)) { - $scoreRecordedMessage = "Your score was not recorded because this problem set is closed."; + $scoreRecordedMessage = "Your score was not recorded because this homework set is closed."; } else { $scoreRecordedMessage = "Your score was not recorded."; } @@ -1016,14 +1016,14 @@ if (before($set->open_date) or after($set->due_date)) { $setClosed = 1; if (before($set->open_date)) { - $setClosedMessage = "This problem set is not yet open."; + $setClosedMessage = "This homework set is not yet open."; } elsif (after($set->due_date)) { - $setClosedMessage = "This problem set is closed."; + $setClosedMessage = "This homework set is closed."; } } #if (before($set->open_date) or after($set->due_date)) { # $setClosed = 1; - # $setClosedMessage = "This problem set is closed."; + # $setClosedMessage = "This homework set is closed."; # if ($authz->hasPermissions($user, "view_answers")) { # $setClosedMessage .= " However, since you are a privileged user, additional attempts will be recorded."; # } else { Index: GatewayQuiz.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/GatewayQuiz.pm,v retrieving revision 1.9 retrieving revision 1.10 diff -Llib/WeBWorK/ContentGenerator/GatewayQuiz.pm -Llib/WeBWorK/ContentGenerator/GatewayQuiz.pm -u -r1.9 -r1.10 --- lib/WeBWorK/ContentGenerator/GatewayQuiz.pm +++ lib/WeBWorK/ContentGenerator/GatewayQuiz.pm @@ -220,7 +220,7 @@ my $ce = $self->{ce}; my $root = $ce->{webworkURLs}->{root}; my $courseName = $ce->{courseName}; - my @links = ("Problem Sets" , "$root/$courseName", "navUp"); + my @links = ("Homework Sets" , "$root/$courseName", "navUp"); my $tail = ""; return $self->navMacro($args, $tail, @links); @@ -229,7 +229,7 @@ sub body { my ($self) = @_; - return CGI::p(CGI::font({-color=>"red"}, "This problem is not available because the problem set that contains it is not yet open.")) + return CGI::p(CGI::font({-color=>"red"}, "This problem is not available because the homework set that contains it is not yet open.")) unless $self->{isOpen}; # unpack some useful variables @@ -249,7 +249,7 @@ # coerce form fields into CGI::Vars format - return CGI::p(CGI::font({-color=>"red"}, "This problem set is not available because it is not yet open.")) + return CGI::p(CGI::font({-color=>"red"}, "This homework set is not available because it is not yet open.")) unless ($self->{isOpen}); print CGI::h3("This is an experimental gateway quiz format"); Index: ProblemSet.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/ProblemSet.pm,v retrieving revision 1.61 retrieving revision 1.62 diff -Llib/WeBWorK/ContentGenerator/ProblemSet.pm -Llib/WeBWorK/ContentGenerator/ProblemSet.pm -u -r1.61 -r1.62 --- lib/WeBWorK/ContentGenerator/ProblemSet.pm +++ lib/WeBWorK/ContentGenerator/ProblemSet.pm @@ -98,7 +98,7 @@ #my $problemSetsPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSets", courseID => $courseID); my $problemSetsPage = $urlpath->parent; - my @links = ("Problem Sets" , $r->location . $problemSetsPage->path, "navUp"); + my @links = ("Homework Sets" , $r->location . $problemSetsPage->path, "navUp"); my $tail = "&displayMode=".$self->{displayMode}."&showOldAnswers=".$self->{will}->{showOldAnswers}; return $self->navMacro($args, $tail, @links); } @@ -123,7 +123,7 @@ } print CGI::start_ul({class=>"LinksMenu"}); print CGI::start_li(); - print CGI::span({style=>"font-size:larger"}, "Problem Sets"); + print CGI::span({style=>"font-size:larger"}, "Homework Sets"); print CGI::start_ul(); # FIXME: setIDs contain no info on published/unpublished so unpublished sets are still printed @@ -254,12 +254,12 @@ if ($self->{invalidSet}) { return CGI::div({class=>"ResultsWithError"}, - CGI::p("The selected problem set ($setName) is not a valid set for $effectiveUser.")); + CGI::p("The selected homework set ($setName) is not a valid set for $effectiveUser.")); } unless ($self->{isOpen}) { return CGI::div({class=>"ResultsWithError"}, - CGI::p("This problem set is not available because it is not yet open.")); + CGI::p("This homework set is not available because it is not yet open.")); } #my $hardcopyURL = @@ -271,7 +271,7 @@ courseID => $courseID, setID => $setName); my $hardcopyURL = $self->systemLink($hardcopyPage); - print CGI::p(CGI::a({href=>$hardcopyURL}, "Download a hardcopy of this problem set.")); + print CGI::p(CGI::a({href=>$hardcopyURL}, "Download a hardcopy of this homework set.")); print CGI::start_table(); print CGI::Tr( Index: Feedback.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/Feedback.pm,v retrieving revision 1.25 retrieving revision 1.26 diff -Llib/WeBWorK/ContentGenerator/Feedback.pm -Llib/WeBWorK/ContentGenerator/Feedback.pm -u -r1.25 -r1.26 --- lib/WeBWorK/ContentGenerator/Feedback.pm +++ lib/WeBWorK/ContentGenerator/Feedback.pm @@ -255,7 +255,7 @@ } if ($set and $verbosity >= 1) { print $MAIL - "***** Data about the problem set: *****\n\n", + "***** Data about the homework set: *****\n\n", $set->toString(), "\n\n"; } Index: Hardcopy.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/Hardcopy.pm,v retrieving revision 1.54 retrieving revision 1.55 diff -Llib/WeBWorK/ContentGenerator/Hardcopy.pm -Llib/WeBWorK/ContentGenerator/Hardcopy.pm -u -r1.54 -r1.55 --- lib/WeBWorK/ContentGenerator/Hardcopy.pm +++ lib/WeBWorK/ContentGenerator/Hardcopy.pm @@ -223,7 +223,7 @@ print CGI::h2("Compile Errors"); print CGI::p(<<EOF); WeBWorK has encountered one or more errors while attempting to process -these problem sets. It is likely that there are errors in the problems +these homework sets. It is likely that there are errors in the problems themselves. If you are a student, contact your professor to have the errors corrected. If you are a professor, please consult the error output below for more information. @@ -243,7 +243,7 @@ print CGI::h2("Software Warnings"); print CGI::p(<<EOF); WeBWorK has encountered one or more warnings while attempting to process these -problem sets. It is likely that this indicates errors or ambiguitiees in the +homework sets. It is likely that this indicates errors or ambiguitiees in the problems themselves. If you are a student, contact your professor to have the problems corrected. If you are a professor, please consut the warning output below for more informaiton. @@ -269,7 +269,7 @@ $aa= ' '; } - print CGI::start_p(), "Select the problem set$ss for which to generate${aa}hardcopy version$ss."; + print CGI::start_p(), "Select the homework set$ss for which to generate${aa}hardcopy version$ss."; if ($authz->hasPermissions($userID, "download_hardcopy_multiuser")) { print "You may also select multiple users from the users list. You will receive hardcopy for each (set, user) pair."; } @@ -290,7 +290,7 @@ print CGI::start_form(-method=>"POST", -action=>$actionURL); print $self->hidden_authen_fields(); print CGI::h3("Options"); - print CGI::p("You may choose to show any of the following data. Correct answers and solutions are only available $phrase_for_privileged_users after the answer date of the problem set."); + print CGI::p("You may choose to show any of the following data. Correct answers and solutions are only available $phrase_for_privileged_users after the answer date of the homework set."); print CGI::p( CGI::checkbox( -name => "showCorrectAnswers", Index: UsersAssignedToSet.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/Instructor/UsersAssignedToSet.pm,v retrieving revision 1.15 retrieving revision 1.16 diff -Llib/WeBWorK/ContentGenerator/Instructor/UsersAssignedToSet.pm -Llib/WeBWorK/ContentGenerator/Instructor/UsersAssignedToSet.pm -u -r1.15 -r1.16 --- lib/WeBWorK/ContentGenerator/Instructor/UsersAssignedToSet.pm +++ lib/WeBWorK/ContentGenerator/Instructor/UsersAssignedToSet.pm @@ -31,7 +31,7 @@ sub initialize { my ($self) = @_; my $r = $self->r; - my $urlpath = $r->urlpath; + my $urlpath = $r->urlpath; my $authz = $r->authz; my $db = $r->db; my $setID = $urlpath->arg("setID"); @@ -113,7 +113,7 @@ return CGI::div({class=>"ResultsWithError"}, CGI::p("You are not authorized to acces the Instructor tools.")) unless $authz->hasPermissions($user, "access_instructor_tools"); - return CGI::div({class=>"ResultsWithError"}, CGI::p("You are not authorized to assign problem sets.")) + return CGI::div({class=>"ResultsWithError"}, CGI::p("You are not authorized to assign homework sets.")) unless $authz->hasPermissions($user, "assign_problem_sets"); my @users = $db->listUsers; @@ -126,7 +126,7 @@ "There is NO undo for unassigning students. "), CGI::p("When you unassign by unchecking a student's name, you destroy all - of the data for problem set $setID for this student. You will then need to + of the data for homework set $setID for this student. You will then need to reassign the set to these students and they will receive new versions of the problems. Make sure this is what you want to do before unchecking students." ); @@ -210,7 +210,7 @@ "There is NO undo for this function. Do not use it unless you know what you are doing! When you unassign a student using this button, or by unchecking their name, you destroy all - of the data for problem set $setID for this student.", + of the data for homework set $setID for this student.", CGI::br(), CGI::submit({name=>"unassignFromAll", value=>"Unassign from All Users"}), CGI::radio_group(-name=>"unassignFromAllSafety", -values=>[0,1], -default=>0, -labels=>{0=>'Read only', 1=>'Allow unassign'}), Index: SetsAssignedToUser.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/Instructor/SetsAssignedToUser.pm,v retrieving revision 1.19 retrieving revision 1.20 diff -Llib/WeBWorK/ContentGenerator/Instructor/SetsAssignedToUser.pm -Llib/WeBWorK/ContentGenerator/Instructor/SetsAssignedToUser.pm -u -r1.19 -r1.20 --- lib/WeBWorK/ContentGenerator/Instructor/SetsAssignedToUser.pm +++ lib/WeBWorK/ContentGenerator/Instructor/SetsAssignedToUser.pm @@ -130,7 +130,7 @@ return CGI::div({class=>"ResultsWithError"}, CGI::p("You are not authorized to access the Instructor tools.")) unless $authz->hasPermissions($user, "access_instructor_tools"); - return CGI::div({class=>"ResultsWithError"}, CGI::p("You are not authorized to assign problem sets.")) + return CGI::div({class=>"ResultsWithError"}, CGI::p("You are not authorized to assign homework sets.")) unless $authz->hasPermissions($user, "assign_problem_sets"); # get list of sets @@ -166,7 +166,7 @@ "Do not uncheck a set unless you know what you are doing.", CGI::br(), "There is NO undo for unassigning a set."); - print CGI::p("When you uncheck a problem set (and save the changes), you destroy all + print CGI::p("When you uncheck a homework set (and save the changes), you destroy all of the data for that set for this student. If You then need to reassign the set and the student will receive new versions of the problems. Make sure this is what you want to do before unchecking sets." Index: ProblemList.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/Instructor/ProblemList.pm,v retrieving revision 1.32 retrieving revision 1.33 diff -Llib/WeBWorK/ContentGenerator/Instructor/ProblemList.pm -Llib/WeBWorK/ContentGenerator/Instructor/ProblemList.pm -u -r1.32 -r1.33 --- lib/WeBWorK/ContentGenerator/Instructor/ProblemList.pm +++ lib/WeBWorK/ContentGenerator/Instructor/ProblemList.pm @@ -415,7 +415,7 @@ my %problemOverrideArgs; my @problem_html; my $userSet = $db->getUserSet($editForUser[0], $setName); # checked - die "user problem set $setName not found." unless $userSet; + die "user homework set $setName not found." unless $userSet; if ($forOneUser) { $userProblemRecord = $db->getUserProblem($editForUser[0], $setName, $problem); # checked Index: ProblemSetEditor.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/Instructor/ProblemSetEditor.pm,v retrieving revision 1.63 retrieving revision 1.64 diff -Llib/WeBWorK/ContentGenerator/Instructor/ProblemSetEditor.pm -Llib/WeBWorK/ContentGenerator/Instructor/ProblemSetEditor.pm -u -r1.63 -r1.64 --- lib/WeBWorK/ContentGenerator/Instructor/ProblemSetEditor.pm +++ lib/WeBWorK/ContentGenerator/Instructor/ProblemSetEditor.pm @@ -274,7 +274,7 @@ return CGI::div({class=>"ResultsWithError"}, "You are not authorized to access the Instructor tools.") unless $authz->hasPermissions($r->param("user"), "access_instructor_tools"); - return CGI::div({class=>"ResultsWithError"}, "You are not authorized to modify problem sets.") + return CGI::div({class=>"ResultsWithError"}, "You are not authorized to modify homework sets.") unless $authz->hasPermissions($r->param("user"), "modify_problem_sets"); Index: Assigner.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/Instructor/Assigner.pm,v retrieving revision 1.27 retrieving revision 1.28 diff -Llib/WeBWorK/ContentGenerator/Instructor/Assigner.pm -Llib/WeBWorK/ContentGenerator/Instructor/Assigner.pm -u -r1.27 -r1.28 --- lib/WeBWorK/ContentGenerator/Instructor/Assigner.pm +++ lib/WeBWorK/ContentGenerator/Instructor/Assigner.pm @@ -53,7 +53,7 @@ return CGI::div({class=>"ResultsWithError"}, "You are not authorized to access the Instructor tools.") unless $authz->hasPermissions($user, "access_instructor_tools"); - return CGI::div({class=>"ResultsWithError"}, "You are not authorized to assign problem sets.") + return CGI::div({class=>"ResultsWithError"}, "You are not authorized to assign homework sets.") unless $authz->hasPermissions($user, "assign_problem_sets"); @@ -173,7 +173,7 @@ "There is NO undo for unassigning students. "); print CGI::p("When you unassign a student's name, you destroy all - of the data for that problem set for that student. You will then need to + of the data for that homework set for that student. You will then need to reassign the set(s) to these students and they will receive new versions of the problems. Make sure this is what you want to do before unassigning students." ); Index: SetMaker.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm,v retrieving revision 1.34 retrieving revision 1.35 diff -Llib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm -Llib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm -u -r1.34 -r1.35 --- lib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm +++ lib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm @@ -254,7 +254,7 @@ my $self = shift; my $library_selected = shift; my $list_of_local_sets = shift; - my $default_value = "Select a Problem Set"; + my $default_value = "Select a Homework Set"; if(scalar(@$list_of_local_sets) == 0) { $list_of_local_sets = [NO_LOCAL_SET_STRING]; @@ -414,7 +414,7 @@ if($have_local_sets ==0) { $list_of_local_sets = [NO_LOCAL_SET_STRING]; } elsif (not $set_selected or $set_selected eq SELECT_SET_STRING) { - if ($list_of_local_sets->[0] eq "Select a Problem Set") { + if ($list_of_local_sets->[0] eq "Select a Homework Set") { shift @{$list_of_local_sets}; } unshift @{$list_of_local_sets}, SELECT_SET_STRING; @@ -658,7 +658,7 @@ my $set_to_display = $r->param('library_sets'); if (not defined($set_to_display) - or $set_to_display eq "Select a Problem Set" + or $set_to_display eq "Select a Homework Set" or $set_to_display eq NO_LOCAL_SET_STRING) { $self->addbadmessage("You need to select a set from this course to view."); } else { @@ -831,7 +831,7 @@ sub title { - return "Problem Set Maker"; + return "Homework Set Maker"; } sub body { Index: Index.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/Instructor/Index.pm,v retrieving revision 1.47 retrieving revision 1.48 diff -Llib/WeBWorK/ContentGenerator/Instructor/Index.pm -Llib/WeBWorK/ContentGenerator/Instructor/Index.pm -u -r1.47 -r1.48 --- lib/WeBWorK/ContentGenerator/Instructor/Index.pm +++ lib/WeBWorK/ContentGenerator/Instructor/Index.pm @@ -258,9 +258,9 @@ push @error, "You are not allowed to act as a student." if (defined param $r "act_as_user" and not $authz->hasPermissions($userID, "become_student")); - push @error, "You are not allowed to modify problem sets." + push @error, "You are not allowed to modify homework sets." if ((defined param $r "edit_sets" or defined param $r "edit_set_for_users") and not $authz->hasPermissions($userID, "modify_problem_sets")); - push @error, "You are not allowed to assign problem sets." + push @error, "You are not allowed to assign homework sets." if ((defined param $r "sets_assigned_to_user" or defined param $r "users_assigned_to_set") and not $authz->hasPermissions($userID, "assign_problem_sets")); push @error, "You are not allowed to modify student data." if ((defined param $r "edit_users" or defined param $r "user_options" or defined param $r "user_options") and not $authz->hasPermissions($userID, "modify_student_data")); Index: ProblemSetList.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/Instructor/ProblemSetList.pm,v retrieving revision 1.81 retrieving revision 1.82 diff -Llib/WeBWorK/ContentGenerator/Instructor/ProblemSetList.pm -Llib/WeBWorK/ContentGenerator/Instructor/ProblemSetList.pm -u -r1.81 -r1.82 --- lib/WeBWorK/ContentGenerator/Instructor/ProblemSetList.pm +++ lib/WeBWorK/ContentGenerator/Instructor/ProblemSetList.pm @@ -283,7 +283,7 @@ $self->{editMode} = $r->param("editMode") || 0; - return CGI::div({class=>"ResultsWithError"}, CGI::p("You are not authorized to modify problem sets.")) + return CGI::div({class=>"ResultsWithError"}, CGI::p("You are not authorized to modify homework sets.")) if $self->{editMode} and not $authz->hasPermissions($user, "modify_problem_sets"); $self->{exportMode} = $r->param("exportMode") || 0; |
From: Sam H. v. a. <we...@ma...> - 2005-07-05 18:23:27
|
Log Message: ----------- Added support for the tthPreamble.tex file. The effect of this code is the same as the corresponding code in dangerousMacros.pl:tth(), but the technique is slightly different. This is a hack. Eventually, we'd like to have a common out-of-sandbox method of calling TTH. Closes bug #799. Modified Files: -------------- webwork2/lib/WeBWorK/ContentGenerator: Problem.pm Revision Data ------------- Index: Problem.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/Problem.pm,v retrieving revision 1.172 retrieving revision 1.173 diff -Llib/WeBWorK/ContentGenerator/Problem.pm -Llib/WeBWorK/ContentGenerator/Problem.pm -u -r1.172 -r1.173 --- lib/WeBWorK/ContentGenerator/Problem.pm +++ lib/WeBWorK/ContentGenerator/Problem.pm @@ -31,7 +31,7 @@ use WeBWorK::PG; use WeBWorK::PG::ImageGenerator; use WeBWorK::PG::IO; -use WeBWorK::Utils qw(writeLog writeCourseLog encodeAnswers decodeAnswers ref2string makeTempDirectory); +use WeBWorK::Utils qw(readFile writeLog writeCourseLog encodeAnswers decodeAnswers ref2string makeTempDirectory); use WeBWorK::DB::Utils qw(global2user user2global findDefaults); use WeBWorK::Timing; use URI::Escape; @@ -217,11 +217,12 @@ my $fully = ''; my @tableRows = ( $header ); my $numCorrect = 0; + my $tthPreambleCache; foreach my $name (@answerNames) { my $answerResult = $pg->{answers}->{$name}; my $studentAnswer = $answerResult->{student_ans}; # original_student_ans my $preview = ($showAttemptPreview - ? $self->previewAnswer($answerResult, $imgGen) + ? $self->previewAnswer($answerResult, $imgGen, \$tthPreambleCache) : ""); my $correctAnswer = $answerResult->{correct_ans}; my $answerScore = $answerResult->{score}; @@ -277,7 +278,7 @@ sub previewAnswer { - my ($self, $answerResult, $imgGen) = @_; + my ($self, $answerResult, $imgGen, $tthPreambleCache) = @_; my $ce = $self->r->ce; my $effectiveUser = $self->{effectiveUser}; my $set = $self->{set}; @@ -296,9 +297,30 @@ if ($displayMode eq "plainText") { return $tex; } elsif ($displayMode eq "formattedText") { + + # read the TTH preamble, or use the cached copy passed in from the caller + my $tthPreamble; + if (defined $$tthPreambleCache) { + $tthPreamble = $$tthPreambleCache; + } else { + my $tthPreambleFile = $ce->{courseDirs}->{templates} . "/tthPreamble.tex"; + if (-r $tthPreambleFile) { + $tthPreamble = readFile($tthPreambleFile); + # thanks to Jim Martino. each line in the definition file should end with + #a % to prevent adding supurious paragraphs to output: + $tthPreamble =~ s/(.)\n/$1%\n/g; + # solves the problem if the file doesn't end with a return: + $tthPreamble .="%\n"; + # store preamble in cache: + $$tthPreambleCache = $tthPreamble; + } else { + } + } + + # construct TTH command line my $tthCommand = $ce->{externalPrograms}->{tth} . " -L -f5 -r 2> /dev/null <<END_OF_INPUT; echo > /dev/null\n" - . "\\(".$tex."\\)\n" + . $tthPreamble . "\\(" . $tex . "\\)\n" . "END_OF_INPUT\n"; # call tth @@ -308,6 +330,7 @@ } else { return $result; } + } elsif ($displayMode eq "images") { $imgGen->add($tex); } elsif ($displayMode eq "jsMath") { |
From: Sam H. v. a. <we...@ma...> - 2005-07-05 18:19:21
|
Log Message: ----------- fixed tiny indentation problem Modified Files: -------------- webwork2/lib/WeBWorK: PG.pm Revision Data ------------- Index: PG.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/PG.pm,v retrieving revision 1.60 retrieving revision 1.61 diff -Llib/WeBWorK/PG.pm -Llib/WeBWorK/PG.pm -u -r1.60 -r1.61 --- lib/WeBWorK/PG.pm +++ lib/WeBWorK/PG.pm @@ -137,8 +137,8 @@ # ADDED: dvipngTempDir # ADDED: jsMathURL # ADDED: asciimathURL - # ADDED: macrosPath - # REMOVED: macrosDirectory, courseScriptsDirectory + # ADDED: macrosPath + # REMOVED: macrosDirectory, courseScriptsDirectory $envir{cgiDirectory} = undef; $envir{cgiURL} = undef; |
From: dpvc v. a. <we...@ma...> - 2005-07-05 17:57:55
|
Log Message: ----------- Fixed incorrect method call to SaveHiidenFlags (should have been HiddenFlags). Modified Files: -------------- webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor: FileManager.pm Revision Data ------------- Index: FileManager.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/FileManager.pm,v retrieving revision 1.10 retrieving revision 1.11 diff -Llib/WeBWorK/ContentGenerator/Instructor/FileManager.pm -Llib/WeBWorK/ContentGenerator/Instructor/FileManager.pm -u -r1.10 -r1.11 --- lib/WeBWorK/ContentGenerator/Instructor/FileManager.pm +++ lib/WeBWorK/ContentGenerator/Instructor/FileManager.pm @@ -513,8 +513,8 @@ ), ]); print CGI::end_table(); - print CGI::hidden({name=>"files", value=>$file}); - $self->SaveHiddenFlags; + print CGI::hidden({name=>"files", value=>$file}); + $self->HiddenFlags; } ################################################## |
From: dpvc v. a. <we...@ma...> - 2005-07-05 01:46:45
|
Log Message: ----------- Don't load Parser.pl (it causes trouble with the Matrix.pm module). We only needed it because of the calls to Real() that were being used by Formula objects, so have them call Value::Real directly. be sure to get the latest versions of Parser and Value (including the top-level Parser.pm and Value.pm) in order to use this update. Modified Files: -------------- pg/lib/Parser/Legacy: PGanswermacros.pl Revision Data ------------- Index: PGanswermacros.pl =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser/Legacy/PGanswermacros.pl,v retrieving revision 1.3 retrieving revision 1.4 diff -Llib/Parser/Legacy/PGanswermacros.pl -Llib/Parser/Legacy/PGanswermacros.pl -u -r1.3 -r1.4 --- lib/Parser/Legacy/PGanswermacros.pl +++ lib/Parser/Legacy/PGanswermacros.pl @@ -133,8 +133,8 @@ $useBaseTenLog , $inputs_ref , $QUESTIONNAIRE_ANSWERS , - $user_context , - $Context , + $user_context, + $Context, ); @@ -167,11 +167,8 @@ $QUESTIONNAIRE_ANSWERS = ''; if (!main::PG_restricted_eval(q!$main::useOldAnswerMacros!)) { - # - # Force loading of Parser.pl and get the Context command - # - main::PG_restricted_eval(q!loadMacros("Parser.pl")!); - $Context = main::PG_restricted_eval(q!\&Context!); + $user_context = main::PG_restricted_eval(q!\%context!); + $Context = sub {Parser::Context->current($user_context,@_)}; } } |
From: dpvc v. a. <we...@ma...> - 2005-07-05 01:41:26
|
Log Message: ----------- Modified the perl() method to make calles to Value:: directly rather than to the main:: stubs for creating Value objects. This will make it work better from within packages or when Parser.pl hasn't been loaded. A few things still won't work in this case; e.g., calls to Closed() for intervals (these could be handled better using ->with()) and to functions like Factorial and log10 that are defined in Parser.pl. Finally, there should be better object-based control over what routines are called to create these objects, so that subclasses of the Value objects will be able to be generated correctly. Modified Files: -------------- pg/lib: Parser.pm Value.pm pg/lib/Parser: List.pm pg/lib/Parser/BOP: union.pm pg/lib/Parser/List: Interval.pm Matrix.pm Revision Data ------------- Index: Value.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value.pm,v retrieving revision 1.37 retrieving revision 1.38 diff -Llib/Value.pm -Llib/Value.pm -u -r1.37 -r1.38 --- lib/Value.pm +++ lib/Value.pm @@ -461,7 +461,7 @@ sub string {shift->value} sub TeX {shift->string(@_)} # -# For perl, call the appropriate constructor around the objects data +# For perl, call the appropriate constructor around the object's data # sub perl { my $self = shift; my $parens = shift; my $matrix = shift; @@ -476,7 +476,7 @@ $perl = join(',',@p); $perl = '['.$perl.']' if $mtype > 0; } else { - $perl = $class.'('.join(',',@p).')'; + $perl = 'new '.ref($self).'('.join(',',@p).')'; $perl = '('.$perl.')' if $parens == 1; } return $perl; Index: Parser.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser.pm,v retrieving revision 1.23 retrieving revision 1.24 diff -Llib/Parser.pm -Llib/Parser.pm -u -r1.23 -r1.24 --- lib/Parser.pm +++ lib/Parser.pm @@ -625,7 +625,7 @@ my $self = shift; $self->setValues(@_); my $perl = $self->{tree}->perl; - $perl = 'Real('.$perl.')' if $self->isRealNumber; + $perl = 'new Value::Real('.$perl.')' if $self->isRealNumber; return $perl; } Index: List.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser/List.pm,v retrieving revision 1.12 retrieving revision 1.13 diff -Llib/Parser/List.pm -Llib/Parser/List.pm -u -r1.12 -r1.13 --- lib/Parser/List.pm +++ lib/Parser/List.pm @@ -227,7 +227,7 @@ my $self = shift; my $parens = shift; my $matrix = shift; my $perl; my @p = (); foreach my $x (@{$self->{coords}}) {push(@p,$x->perl)} - $perl = $self->type.'('.join(',',@p).')'; + $perl = 'new Value::'.$self->type.'('.join(',',@p).')'; $perl = 'Closed('.$perl.')' if $self->{canBeInterval} && $self->{open}.$self->{close} eq '[]'; $perl = '('.$perl.')' if $parens; Index: union.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser/BOP/union.pm,v retrieving revision 1.4 retrieving revision 1.5 diff -Llib/Parser/BOP/union.pm -Llib/Parser/BOP/union.pm -u -r1.4 -r1.5 --- lib/Parser/BOP/union.pm +++ lib/Parser/BOP/union.pm @@ -38,7 +38,7 @@ sub perl { my $self = shift; my $parens = shift; my @union = (); foreach my $x ($self->makeUnion) {push(@union,$x->perl)} - my $perl = 'Union('.join(',',@union).')'; + my $perl = 'new Value::Union('.join(',',@union).')'; $perl = '('.$perl.')' if $parens; return $perl; } Index: Interval.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser/List/Interval.pm,v retrieving revision 1.6 retrieving revision 1.7 diff -Llib/Parser/List/Interval.pm -Llib/Parser/List/Interval.pm -u -r1.6 -r1.7 --- lib/Parser/List/Interval.pm +++ lib/Parser/List/Interval.pm @@ -45,7 +45,7 @@ my $self = shift; my $parens = shift; my $perl; my @p = (); foreach my $x (@{$self->{coords}}) {push(@p,$x->perl)} - $perl = $self->type.'('.join(',',"'".$self->{open}."'",@p,"'".$self->{close}."'").')'; + $perl = 'new Value::'.$self->type.'('.join(',',"'".$self->{open}."'",@p,"'".$self->{close}."'").')'; $perl = '('.$perl.')' if $parens; return $perl; } Index: Matrix.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser/List/Matrix.pm,v retrieving revision 1.3 retrieving revision 1.4 diff -Llib/Parser/List/Matrix.pm -Llib/Parser/List/Matrix.pm -u -r1.3 -r1.4 --- lib/Parser/List/Matrix.pm +++ lib/Parser/List/Matrix.pm @@ -50,7 +50,7 @@ if ($matrix) { $perl = '['.join(',',@p).']'; } else { - $perl = $self->type.'('.join(',',@p).')'; + $perl = 'new Value::'.$self->type.'('.join(',',@p).')'; $perl = '('.$perl.')' if $parens; } return $perl; |
From: Mike G. v. a. <we...@ma...> - 2005-07-05 00:35:02
|
Log Message: ----------- Modified formatting Modified Files: -------------- pg/lib/Parser/Legacy: PGanswermacros.pl Revision Data ------------- Index: PGanswermacros.pl =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser/Legacy/PGanswermacros.pl,v retrieving revision 1.2 retrieving revision 1.3 diff -Llib/Parser/Legacy/PGanswermacros.pl -Llib/Parser/Legacy/PGanswermacros.pl -u -r1.2 -r1.3 --- lib/Parser/Legacy/PGanswermacros.pl +++ lib/Parser/Legacy/PGanswermacros.pl @@ -133,8 +133,8 @@ $useBaseTenLog , $inputs_ref , $QUESTIONNAIRE_ANSWERS , -$user_context, -$Context, + $user_context , + $Context , ); |
From: dpvc v. a. <we...@ma...> - 2005-07-05 00:11:59
|
Log Message: ----------- Main file that calls in the legacy modules. Added Files: ----------- pg/lib/Parser: Legacy.pm Revision Data ------------- --- /dev/null +++ lib/Parser/Legacy.pm @@ -0,0 +1,8 @@ +# +# Load all the legacy code +# + +use Parser::Legacy::NumberWithUnits; +use Parser::Legacy::LimitedNumeric; + +1; |
From: Mike G. v. a. <we...@ma...> - 2005-07-04 22:14:18
|
Log Message: ----------- Add comment Modified Files: -------------- pg/lib: AnswerHash.pm Revision Data ------------- Index: AnswerHash.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/AnswerHash.pm,v retrieving revision 1.8 retrieving revision 1.9 diff -Llib/AnswerHash.pm -Llib/AnswerHash.pm -u -r1.8 -r1.9 --- lib/AnswerHash.pm +++ lib/AnswerHash.pm @@ -505,7 +505,7 @@ } elsif (ref($input) eq 'ARRAY' ) { # sometimes the answer may already be decoded into an array. my @input = @$input; $self-> {rh_ans} -> {original_student_ans} = " ( " .join(", ",@input) . " ) "; - $input = \@input; #make a local copy + $input = \@input; $self-> {rh_ans} -> {student_ans} = $input; } else { |
From: dpvc v. a. <we...@ma...> - 2005-07-04 20:21:45
|
Log Message: ----------- Folded in Mike's changes to checkbox_cmp. Modified Files: -------------- pg/lib/Parser/Legacy: PGanswermacros.pl Revision Data ------------- Index: PGanswermacros.pl =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser/Legacy/PGanswermacros.pl,v retrieving revision 1.1 retrieving revision 1.2 diff -Llib/Parser/Legacy/PGanswermacros.pl -Llib/Parser/Legacy/PGanswermacros.pl -u -r1.1 -r1.2 --- lib/Parser/Legacy/PGanswermacros.pl +++ lib/Parser/Legacy/PGanswermacros.pl @@ -2483,6 +2483,7 @@ sub ignore_order { my $rh_ans = shift; die "expected an answer hash" unless ref($rh_ans)=~/HASH/i; + $rh_ans->{_filter_name} = 'ignore_order'; $rh_ans->{student_ans} = join( "", lex_sort( split( /\s*/, $rh_ans->{student_ans} ) ) ); $rh_ans->{correct_ans} = join( "", lex_sort( split( /\s*/, $rh_ans->{correct_ans} ) ) ); @@ -2927,40 +2928,117 @@ # rather than as a \0 delimited string. sub checkbox_cmp { my $correctAnswer = shift @_; - $correctAnswer = str_filters( $correctAnswer, 'ignore_order' ); - - my $answer_evaluator = sub { - my $in = shift @_; - $in = '' unless defined $in; #in case no boxes checked - # multiple answers could come in two forms - # either a \0 delimited string or - # an array reference. We handle both. - if (ref($in) eq 'ARRAY') { - $in = join("",@{$in}); # convert array to single no-delimiter string - } else { - my @temp = split( "\0", $in ); #convert "\0"-delimited string to array... - $in = join( "", @temp ); #and then to a single no-delimiter string - } - my $original_student_ans = $in; #well, almost original - $in = str_filters( $in, 'ignore_order' ); - - my $correctQ = ($in eq $correctAnswer) ? 1: 0; - - my $ans_hash = new AnswerHash( - 'score' => $correctQ, - 'correct_ans' => "$correctAnswer", - 'student_ans' => $in, - 'ans_message' => "", - 'type' => "checkbox_cmp", - 'preview_text_string' => $in, - 'preview_latex_string' => $in, - 'original_student_ans' => $original_student_ans - ); - return $ans_hash; - - }; + my %options = @_; + assign_option_aliases( \%options, + ); + set_default_options( \%options, + 'debug' => 0, + 'type' => 'checkbox_cmp', + ); + my $answer_evaluator = new AnswerEvaluator( + correct_ans => $correctAnswer, + type => $options{type}, + ); + # pass along debug requests + $answer_evaluator->{debug} = $options{debug}; + + # join student answer array into a single string if necessary + $answer_evaluator->install_pre_filter(sub { + my $rh_ans = shift; + $rh_ans->{_filter_name} = 'convert student_ans to string'; + $rh_ans->{student_ans} = join("", @{$rh_ans->{student_ans}}) + if ref($rh_ans->{student_ans}) =~/ARRAY/i; + $rh_ans; + }); + # ignore order of check boxes + $answer_evaluator->install_pre_filter(\&ignore_order); + # compare as strings + $answer_evaluator->install_evaluator(sub { + my $rh_ans = shift; + $rh_ans->{_filter_name} = 'compare strings generated by checked boxes'; + $rh_ans->{score} = ($rh_ans->{student_ans} eq $rh_ans->{correct_ans}) ? 1 : 0; + $rh_ans; + }); + # fix up preview displays + $answer_evaluator->install_post_filter( sub { + my $rh_ans = shift; + $rh_ans->{_filter_name} = 'adjust preview strings'; + $rh_ans->{type} = $options{type}; + $rh_ans->{preview_text_string} = '\\text{'.$rh_ans->{student_ans}.'}', + $rh_ans->{preview_latex_string} = '\\text{'.$rh_ans->{student_ans}.'}', + $rh_ans; + + + }); + +# my $answer_evaluator = sub { +# my $in = shift @_; +# $in = '' unless defined $in; #in case no boxes checked +# # multiple answers could come in two forms +# # either a \0 delimited string or +# # an array reference. We handle both. +# if (ref($in) eq 'ARRAY') { +# $in = join("",@{$in}); # convert array to single no-delimiter string +# } else { +# my @temp = split( "\0", $in ); #convert "\0"-delimited string to array... +# $in = join( "", @temp ); #and then to a single no-delimiter string +# } +# my $original_student_ans = $in; #well, almost original +# $in = str_filters( $in, 'ignore_order' ); +# +# my $correctQ = ($in eq $correctAnswer) ? 1: 0; +# +# my $ans_hash = new AnswerHash( +# 'score' => $correctQ, +# 'correct_ans' => "$correctAnswer", +# 'student_ans' => $in, +# 'ans_message' => "", +# 'type' => "checkbox_cmp", +# 'preview_text_string' => $in, +# 'preview_latex_string' => $in, +# 'original_student_ans' => $original_student_ans +# ); +# return $ans_hash; +# +# }; return $answer_evaluator; } +# sub checkbox_cmp { +# my $correctAnswer = shift @_; +# $correctAnswer = str_filters( $correctAnswer, 'ignore_order' ); +# +# my $answer_evaluator = sub { +# my $in = shift @_; +# $in = '' unless defined $in; #in case no boxes checked +# # multiple answers could come in two forms +# # either a \0 delimited string or +# # an array reference. We handle both. +# if (ref($in) eq 'ARRAY') { +# $in = join("",@{$in}); # convert array to single no-delimiter string +# } else { +# my @temp = split( "\0", $in ); #convert "\0"-delimited string to array... +# $in = join( "", @temp ); #and then to a single no-delimiter string +# } +# my $original_student_ans = $in; #well, almost original +# $in = str_filters( $in, 'ignore_order' ); +# +# my $correctQ = ($in eq $correctAnswer) ? 1: 0; +# +# my $ans_hash = new AnswerHash( +# 'score' => $correctQ, +# 'correct_ans' => "$correctAnswer", +# 'student_ans' => $in, +# 'ans_message' => "", +# 'type' => "checkbox_cmp", +# 'preview_text_string' => $in, +# 'preview_latex_string' => $in, +# 'original_student_ans' => $original_student_ans +# ); +# return $ans_hash; +# +# }; +# return $answer_evaluator; +# } #added 6/28/2000 by David Etlinger #exactly the same as strict_str_cmp, |
From: dpvc v. a. <we...@ma...> - 2005-07-04 20:15:25
|
Log Message: ----------- This is an attempt at making the traditional answer checkers call the new Parser in place of their original ones. That is, if you follow the instructions in the README file, then problems that use std_num_cmp(), fun_cmp(), etc. will really be using the new Parser instead of the original PGanswermacros.pl versions. The old answer checkers are still available, and can be switched back on a site-wide, course-wide, or problem-by-problem basis. See the README for details. Added Files: ----------- pg/lib/Parser/Legacy: LimitedNumeric.pm NumberWithUnits.pm PGanswermacros.pl README Revision Data ------------- --- /dev/null +++ lib/Parser/Legacy/LimitedNumeric.pm @@ -0,0 +1,87 @@ +########################################################## +# +# Implements a context in which numbers can be entered, +# but no operations are permitted between them. +# +# There are two versions: one a number with no operations, +# and one for fractions of integers. Select them using +# one of the following commands: +# +# Context("LimiteNumeric"); +# Context("LimitedNumeric-Fraction"); +# + + +# +# Minus can only appear in front of a number +# +package Parser::Legacy::LimitedNumeric::UOP::minus; +our @ISA = qw(Parser::UOP::minus); + +sub _check { + my $self = shift; + $self->SUPER::_check; + my $uop = $self->{def}{string} || $self->{uop}; + $self->Error("You can only use '$uop' with (non-negative) numbers") + unless $self->{op}->class =~ /Number|DIVIDE/; +} + +sub class {'MINUS'}; + + +# +# Divides can only appear between numbers or a negative +# number and a number +# +package Parser::Legacy::LimitedNumeric::BOP::divide; +our @ISA = qw(Parser::BOP::divide); + +sub _check { + my $self = shift; + $self->SUPER::_check; + my $bop = $self->{def}{string} || $self->{bop}; + $self->Error("You can only use '$bop' between (non-negative) numbers") + unless $self->{lop}->class =~ /Number|MINUS/ && + $self->{rop}->class eq 'Number'; +} + +sub class {'DIVIDE'}; + + +package Parser::Legacy::LimitedNumeric; + +# +# LimitedNumeric context uses the modified minus +# and removes all other operatots, functions and parentheses +# +my $context = $Parser::Context::Default::context{Numeric}->copy; +$Parser::Context::Default::context{'LimitedNumeric'} = $context; +$context->operators->set('u-' => {class => 'Parser::Legacy::LimitedNumeric::UOP::minus'}); +$context->operators->undefine( + '+', '-', '*', '* ', ' *', ' ', '/', '/ ', ' /', '^', '**', + 'U', '.', '><', 'u+', '!', '_', ',', +); +$context->parens->undefine('|','{','(','['); +$context->functions->disable('All'); + +# +# For the Fraction versions, allow the modified division, and +# make sure numbers are just integers +# +$context = $Parser::Context::Default::context{Numeric}->copy; +$Parser::Context::Default::context{'LimitedNumeric-Fraction'} = $context; +$context->operators->set( + 'u-' => {class => 'Parser::Legacy::LimitedNumeric::UOP::minus'}, + '/' => {class => 'Parser::Legacy::LimitedNumeric::BOP::divide'}, + ' /' => {class => 'Parser::Legacy::LimitedNumeric::BOP::divide'}, + '/ ' => {class => 'Parser::Legacy::LimitedNumeric::BOP::divide'}, +); +$context->operators->undefine( + '+', '-', '*', '* ', ' *', ' ', '^', '**', + 'U', '.', '><', 'u+', '!', '_', ',', +); +$context->parens->undefine('|','{','['); +$context->functions->disable('All'); +Parser::Number::NoDecimals($context); + +1; --- /dev/null +++ lib/Parser/Legacy/PGanswermacros.pl @@ -0,0 +1,4755 @@ +# This file is PGanswermacros.pl +# This includes the subroutines for the ANS macros, that +# is, macros allowing a more flexible answer checking +#################################################################### +# Copyright @ 1995-2000 University of Rochester +# All Rights Reserved +#################################################################### +#$Id: PGanswermacros.pl,v 1.1 2005/07/04 20:12:22 dpvc Exp $ + +=head1 NAME + + PGanswermacros.pl -- located in the courseScripts directory + +=head1 SYNPOSIS + + Number Answer Evaluators: + num_cmp() -- uses an input hash to determine parameters + + std_num_cmp(), std_num_cmp_list(), std_num_cmp_abs, std_num_cmp_abs_list() + frac_num_cmp(), frac_num_cmp_list(), frac_num_cmp_abs, frac_num_cmp_abs_list() + arith_num_cmp(), arith_num_cmp_list(), arith_num_cmp_abs, arith_num_cmp_abs_list() + strict_num_cmp(), strict_num_cmp_list(), strict_num_cmp_abs, strict_num_cmp_abs_list() + numerical_compare_with_units() -- requires units as part of the answer + std_num_str_cmp() -- also accepts a set of strings as possible answers + + Function Answer Evaluators: + fun_cmp() -- uses an input hash to determine parameters + + function_cmp(), function_cmp_abs() + function_cmp_up_to_constant(), function_cmp_up_to_constant_abs() + multivar_function_cmp() + + String Answer Evaluators: + str_cmp() -- uses an input hash to determine parameters + + std_str_cmp(), std_str_cmp_list(), std_cs_str_cmp(), std_cs_str_cmp_list() + strict_str_cmp(), strict_str_cmp_list() + ordered_str_cmp(), ordered_str_cmp_list(), ordered_cs_str_cmp(), ordered_cs_str_cmp_list() + unordered_str_cmp(), unordered_str_cmp_list(), unordered_cs_str_cmp(), unordered_cs_str_cmp_list() + + Miscellaneous Answer Evaluators: + checkbox_cmp() + radio_cmp() + +=cut + +=head1 DESCRIPTION + +This file adds subroutines which create "answer evaluators" for checking +answers. Each answer evaluator accepts a single input from a student answer, +checks it and creates an output hash %ans_hash with seven or eight entries +(the preview_latex_string is optional). The output hash is now being created +with the AnswerHash package "class", which is located at the end of this file. +This class is currently just a wrapper for the hash, but this might change in +the future as new capabilities are added. + + score => $correctQ, + correct_ans => $originalCorrEqn, + student_ans => $modified_student_ans + original_student_ans => $original_student_answer, + ans_message => $PGanswerMessage, + type => 'typeString', + preview_text_string => $preview_text_string, + preview_latex_string => $preview_latex_string + + + $ans_hash{score} -- a number between 0 and 1 indicating + whether the answer is correct. Fractions + allow the implementation of partial + credit for incorrect answers. + $ans_hash{correct_ans} -- The correct answer, as supplied by the + instructor and then formatted. This can + be viewed by the student after the answer date. + $ans_hash{student_ans} -- This is the student answer, after reformatting; + for example the answer might be forced + to capital letters for comparison with + the instructors answer. For a numerical + answer, it gives the evaluated answer. + This is displayed in the section reporting + the results of checking the student answers. + $ans_hash{original_student_ans} -- This is the original student answer. This is displayed + on the preview page and may be used for sticky answers. + $ans_hash{ans_message} -- Any error message, or hint provided by the answer evaluator. + This is also displayed in the section reporting + the results of checking the student answers. + $ans_hash{type} -- A string indicating the type of answer evaluator. This + helps in preprocessing the student answer for errors. + Some examples: + 'number_with_units' + 'function' + 'frac_number' + 'arith_number' + $ans_hash{preview_text_string} -- This typically shows how the student answer was parsed. It is + displayed on the preview page. For a student answer of 2sin(3x) + this would be 2*sin(3*x). For string answers it is typically the + same as $ans_hash{student_ans}. + $ans_hash{preview_latex_string} -- THIS IS OPTIONAL. This is latex version of the student answer + which is used to show a typeset view on the answer on the preview + page. For a student answer of 2/3, this would be \frac{2}{3}. + +Technical note: the routines in this file are not actually answer evaluators. Instead, they create +answer evaluators. An answer evaluator is an anonymous subroutine, referenced by a named scalar. The +routines in this file build the subroutine and return a reference to it. Later, when the student +actually enters an answer, the problem processor feeds that answer to the referenced subroutine, which +evaluates it and returns a score (usually 0 or 1). For most users, this distinction is unimportant, but +if you plan on writing your own answer evaluators, you should understand this point. + +=cut + +BEGIN { + be_strict(); # an alias for use strict. This means that all global variable must contain main:: as a prefix. +} + + +my ($BR , # convenient localizations. + $PAR , + $numRelPercentTolDefault , + $numZeroLevelDefault , + $numZeroLevelTolDefault , + $numAbsTolDefault , + $numFormatDefault , + $functRelPercentTolDefault , + $functZeroLevelDefault , + $functZeroLevelTolDefault , + $functAbsTolDefault , + $functNumOfPoints , + $functVarDefault , + $functLLimitDefault , + $functULimitDefault , + $functMaxConstantOfIntegration , + $CA , + $rh_envir , + $useBaseTenLog , + $inputs_ref , + $QUESTIONNAIRE_ANSWERS , +$user_context, +$Context, +); + + + + +sub _PGanswermacros_init { + + $BR = main::PG_restricted_eval(q!$main::BR!); + $PAR = main::PG_restricted_eval(q!$main::PAR!); + + # import defaults + # these are now imported from the %envir variable + $numRelPercentTolDefault = main::PG_restricted_eval(q!$main::numRelPercentTolDefault!); + $numZeroLevelDefault = main::PG_restricted_eval(q!$main::numZeroLevelDefault!); + $numZeroLevelTolDefault = main::PG_restricted_eval(q!$main::numZeroLevelTolDefault!); + $numAbsTolDefault = main::PG_restricted_eval(q!$main::numAbsTolDefault!); + $numFormatDefault = main::PG_restricted_eval(q!$main::numFormatDefault!); + $functRelPercentTolDefault = main::PG_restricted_eval(q!$main::functRelPercentTolDefault!); + $functZeroLevelDefault = main::PG_restricted_eval(q!$main::functZeroLevelDefault!); + $functZeroLevelTolDefault = main::PG_restricted_eval(q!$main::functZeroLevelTolDefault!); + $functAbsTolDefault = main::PG_restricted_eval(q!$main::functAbsTolDefault!); + $functNumOfPoints = main::PG_restricted_eval(q!$main::functNumOfPoints!); + $functVarDefault = main::PG_restricted_eval(q!$main::functVarDefault!); + $functLLimitDefault = main::PG_restricted_eval(q!$main::functLLimitDefault!); + $functULimitDefault = main::PG_restricted_eval(q!$main::functULimitDefault!); + $functMaxConstantOfIntegration = main::PG_restricted_eval(q!$main::functMaxConstantOfIntegration!); + $rh_envir = main::PG_restricted_eval(q!\%main::envir!); + $useBaseTenLog = main::PG_restricted_eval(q!$main::useBaseTenLog!); + $inputs_ref = main::PG_restricted_eval(q!$main::inputs_ref!); + $QUESTIONNAIRE_ANSWERS = ''; + + if (!main::PG_restricted_eval(q!$main::useOldAnswerMacros!)) { + # + # Force loading of Parser.pl and get the Context command + # + main::PG_restricted_eval(q!loadMacros("Parser.pl")!); + $Context = main::PG_restricted_eval(q!\&Context!); + } +} + + + +########################################################################## + +#Note use $rh_envir to read environment variables + +########################################################################## +## Number answer evaluators + +=head2 Number Answer Evaluators + +Number answer evaluators take in a numerical answer, compare it to the correct answer, +and return a score. In addition, they can choose to accept or reject an answer based on +its format, closeness to the correct answer, and other criteria. There are two types +of numerical answer evaluators: num_cmp(), which takes a hash of named options as parameters, +and the "mode"_num_cmp() variety, which use different functions to access different sets of +options. In addition, there is the special case of std_num_str_cmp(), which can evaluate +both numbers and strings. + +Numerical Comparison Options + + correctAnswer -- This is the correct answer that the student answer will + be compared to. However, this does not mean that the + student answer must match this exactly. How close the + student answer must be is determined by the other + options, especially tolerance and format. + + tolerance -- These options determine how close the student answer + must be to the correct answer to qualify. There are two + types of tolerance: relative and absolute. Relative + tolerances are given in percentages. A relative + tolerance of 1 indicates that the student answer must + be within 1% of the correct answer to qualify as correct. + In other words, a student answer is correct when + abs(studentAnswer - correctAnswer) <= abs(.01*relpercentTol*correctAnswer) + Using absolute tolerance, the student answer must be a + fixed distance from the correct answer to qualify. + For example, an absolute tolerance of 5 means that any + number which is +-5 of the correct answer qualifies as correct. + Final (rarely used) tolerance options are zeroLevel + and zeroLevelTol, used in conjunction with relative + tolerance. if correctAnswer has absolute value less than + or equal to zeroLevel, then the student answer must be, + in absolute terms, within zeroLevelTol of correctAnswer, i.e., + abs(studentAnswer - correctAnswer) <= zeroLevelTol. + In other words, if the correct answer is very near zero, + an absolute tolerance will be used. One must do this to + handle floating point answers very near zero, because of + the inaccuracy of floating point arithmetic. However, the + default values are almost always adequate. + + mode -- This determines the allowable methods for entering an + answer. Answers which do not meet this requirement will + be graded as incorrect, regardless of their numerical + value. The recognized modes are: + 'std' (default) -- allows any expression which evaluates + to a number, including those using + elementary functions like sin() and + exp(), as well as the operations of + arithmetic (+, -, *, /, ^) + 'strict' -- only decimal numbers are allowed + 'frac' -- whole numbers and fractions are allowed + 'arith' -- arithmetic expressions are allowed, but + no functions + Note that all modes allow the use of "pi" and "e" as + constants, and also the use of "E" to represent scientific + notation. + + format -- The format to use when displaying the correct and + submitted answers. This has no effect on how answers are + evaluated; it is only for cosmetic purposes. The + formatting syntax is the same as Perl uses for the sprintf() + function. Format strings are of the form '%m.nx' or '%m.nx#', + where m and n are described below, and x is a formatter. + Esentially, m is the minimum length of the field + (make this negative to left-justify). Note that the decimal + point counts as a character when determining the field width. + If m begins with a zero, the number will be padded with zeros + instead of spaces to fit the field. + The precision specifier (n) works differently, depending + on which formatter you are using. For d, i, o, u, x and X + formatters (non-floating point formatters), n is the minimum + number of digits to display. For e and f, it is the number of + digits that appear after the decimal point (extra digits will + be rounded; insufficient digits will be padded with spaces--see + '#' below). For g, it is the number of significant digits to + display. + The full list of formatters can be found in the manpage + for printf(3), or by typing "perldoc -f sprintf" at a + terminal prompt. The following is a brief summary of the + most frequent formatters: + d -- decimal number + ld -- long decimal number + u -- unsigned decimal number + lu -- long unsigned decimal number + x -- hexadecimal number + o -- octal number + e -- floating point number in scientific notation + f -- floating point number + g -- either e or f, whichever takes less space + Technically, g will use e if the exponent is less than -4 or + greater than or equal to the precision. Trailing zeros are + removed in this mode. + If the format string ends in '#', trailing zeros will be + removed in the decimal part. Note that this is not a standard + syntax; it is handled internally by WeBWorK and not by Perl + (although this should not be a concern to end users). + The default format is '%0.5f#', which displays as a floating + point number with 5 digits of precision and no trailing zeros. + Other useful format strings might be '%0.2f' for displaying + dollar amounts, or '%010d' to display an integer with leading + zeros. Setting format to an empty string ( '' ) means no + formatting will be used; this will show 'arbitrary' precision + floating points. + +Default Values (As of 7/24/2000) (Option -- Variable Name -- Value) + + Format -- $numFormatDefault -- "%0.5f#" + Relative Tolerance -- $numRelPercentTolDefault -- .1 + Absolute Tolerance -- $numAbsTolDefault -- .001 + Zero Level -- $numZeroLevelDefault -- 1E-14 + Zero Level Tolerance -- $numZeroLevelTolDefault -- 1E-12 + +=cut + + +=head3 num_cmp() + +Compares a number or a list of numbers, using a named hash of options to set +parameters. This can make for more readable code than using the "mode"_num_cmp() +style, but some people find one or the other easier to remember. + +ANS( num_cmp( answer or answer_array_ref, options_hash ) ); + + 1. the correct answer, or a reference to an array of correct answers + 2. a hash with the following keys (all optional): + mode -- 'std' (default) (allows any expression evaluating to + a number) + 'strict' (only numbers are allowed) + 'frac' (fractions are allowed) + 'arith' (arithmetic expressions allowed) + format -- '%0.5f#' (default); defines formatting for the + correct answer + tol -- an absolute tolerance, or + relTol -- a relative tolerance + units -- the units to use for the answer(s) + strings -- a reference to an array of strings which are valid + answers (works like std_num_str_cmp() ) + zeroLevel -- if the correct answer is this close to zero, + then zeroLevelTol applies + zeroLevelTol -- absolute tolerance to allow when answer is close + to zero + + debug -- if set to 1, provides verbose listing of + hash entries throughout fliters. + + Returns an answer evaluator, or (if given a reference to an array of + answers), a list of answer evaluators. Note that a reference to an array of + answers results is just a shortcut for writing a separate <code>num_cmp()</code> for each + answer. + +EXAMPLES: + + num_cmp( 5 ) -- correct answer is 5, using defaults + for all options + num_cmp( [5,6,7] ) -- correct answers are 5, 6, and 7, + using defaults for all options + num_cmp( 5, mode => 'strict' ) -- correct answer is 5, mode is strict + num_cmp( [5,6], relTol => 5 ) -- correct answers are 5 and 6, + both with 5% relative tolerance + num_cmp( 6, strings => ["Inf", "Minf", "NaN"] ) + -- correct answer is 6, "Inf", "Minf", + and "NaN" recognized as valid, but + incorrect answers. + num_cmp( "-INF", strings => ["INF", "-INF"] ) + -- correct answer is "-INF", "INF" and + numerical expressions recognized as valid, + but incorrect answers. + + +=cut + +sub num_cmp { + my $correctAnswer = shift @_; + $CA = $correctAnswer; + my @opt = @_; + my %out_options; + +######################################################################### +# Retain this first check for backword compatibility. Allows input of the form +# num_cmp($ans, 1, '%0.5f') but warns against it +######################################################################### + my %known_options = ( + 'mode' => 'std', + 'format' => $numFormatDefault, + 'tol' => $numAbsTolDefault, + 'relTol' => $numRelPercentTolDefault, + 'units' => undef, + 'strings' => undef, + 'zeroLevel' => $numZeroLevelDefault, + 'zeroLevelTol' => $numZeroLevelTolDefault, + 'tolType' => 'relative', + 'tolerance' => 1, + 'reltol' => undef, #alternate spelling + 'unit' => undef, #alternate spelling + 'debug' => 0 + ); + + my @output_list; + my( $relPercentTol, $format, $zeroLevel, $zeroLevelTol) = @opt; + + unless( ref($correctAnswer) eq 'ARRAY' || scalar( @opt ) == 0 || + ( defined($opt[0]) and exists $known_options{$opt[0]} ) ) { + # unless the first parameter is a list of arrays + # or the second parameter is a known option or + # no options were used, + # use the old num_cmp which does not use options, but has inputs + # $relPercentTol,$format,$zeroLevel,$zeroLevelTol + warn "This method of using num_cmp() is deprecated. Please rewrite this" . + " problem using the options style of parameter passing (or" . + " check that your first option is spelled correctly)."; + + %out_options = ( 'relTol' => $relPercentTol, + 'format' => $format, + 'zeroLevel' => $zeroLevel, + 'zeroLevelTol' => $zeroLevelTol, + 'mode' => 'std' + ); + } + +######################################################################### +# Now handle the options assuming they are entered in the form +# num_cmp($ans, relTol=>1, format=>'%0.5f') +######################################################################### + %out_options = @opt; + assign_option_aliases( \%out_options, + 'reltol' => 'relTol', + 'unit' => 'units', + 'abstol' => 'tol', + ); + + set_default_options( \%out_options, + 'tolType' => (defined($out_options{'tol'}) ) ? 'absolute' : 'relative', # the existence of "tol" means that we use absolute tolerance mode + 'tolerance' => (defined($out_options{'tolType'}) && $out_options{'tolType'} eq 'absolute' ) ? $numAbsTolDefault : $numRelPercentTolDefault, # relative tolerance is the default + 'mode' => 'std', + 'format' => $numFormatDefault, + 'tol' => undef, + 'relTol' => undef, + 'units' => undef, + 'strings' => undef, + 'zeroLevel' => $numZeroLevelDefault, + 'zeroLevelTol' => $numZeroLevelTolDefault, + 'debug' => 0, + ); + + # can't use both units and strings + if( defined( $out_options{'units'} ) && defined( $out_options{'strings'} ) ) { + warn "Can't use both 'units' and 'strings' in the same problem " . + "(check your parameters to num_cmp() )"; + } + + # absolute tolType and relTol are incompatible. So are relative tolType and tol + if( defined( $out_options{'relTol'} ) && $out_options{'tolType'} eq 'absolute' ) { + warn "The 'tolType' 'absolute' is not compatible with 'relTol' " . + "(check your parameters to num_cmp() )"; + } + if( defined( $out_options{'tol'} ) && $out_options{'tolType'} eq 'relative' ) { + warn "The 'tolType' 'relative' is not compatible with 'tol' " . + "(check your parameters to num_cmp() )"; + } + + + # Handle legacy options + if ($out_options{tolType} eq 'absolute') { + $out_options{'tolerance'}=$out_options{'tol'} if defined($out_options{'tol'}); + delete($out_options{'relTol'}) if exists( $out_options{'relTol'} ); + } else { + $out_options{'tolerance'}=$out_options{'relTol'} if defined($out_options{'relTol'}); + # delete($out_options{'tol'}) if exists( $out_options{'tol'} ); + } + # end legacy options + + # thread over lists + my @ans_list = (); + + if ( ref($correctAnswer) eq 'ARRAY' ) { + @ans_list = @{$correctAnswer}; + } + else { push( @ans_list, $correctAnswer ); + } + + # produce answer evaluators + foreach my $ans (@ans_list) { + if( defined( $out_options{'units'} ) ) { + $ans = "$ans $out_options{'units'}"; + + push( @output_list, NUM_CMP( 'correctAnswer' => $ans, + 'tolerance' => $out_options{'tolerance'}, + 'tolType' => $out_options{'tolType'}, + 'format' => $out_options{'format'}, + 'mode' => $out_options{'mode'}, + 'zeroLevel' => $out_options{'zeroLevel'}, + 'zeroLevelTol' => $out_options{'zeroLevelTol'}, + 'debug' => $out_options{'debug'}, + 'units' => $out_options{'units'}, + ) + ); + } elsif( defined( $out_options{'strings'} ) ) { + + + push( @output_list, NUM_CMP( 'correctAnswer' => $ans, + 'tolerance' => $out_options{tolerance}, + 'tolType' => $out_options{tolType}, + 'format' => $out_options{'format'}, + 'mode' => $out_options{'mode'}, + 'zeroLevel' => $out_options{'zeroLevel'}, + 'zeroLevelTol' => $out_options{'zeroLevelTol'}, + 'debug' => $out_options{'debug'}, + 'strings' => $out_options{'strings'}, + ) + ); + } else { + push(@output_list, + NUM_CMP( 'correctAnswer' => $ans, + 'tolerance' => $out_options{tolerance}, + 'tolType' => $out_options{tolType}, + 'format' => $out_options{'format'}, + 'mode' => $out_options{'mode'}, + 'zeroLevel' => $out_options{'zeroLevel'}, + 'zeroLevelTol' => $out_options{'zeroLevelTol'}, + 'debug' => $out_options{'debug'}, + ), + ); + } + } + + return (wantarray) ? @output_list : $output_list[0]; +} + +#legacy code for compatability purposes +sub num_rel_cmp { # compare numbers + std_num_cmp( @_ ); +} + + +=head3 "mode"_num_cmp() functions + +There are 16 functions total, 4 for each mode (std, frac, strict, arith). Each mode has +one "normal" function, one which accepts a list of answers, one which uses absolute +rather than relative tolerance, and one which uses absolute tolerance and accepts a list. +The "std" family is documented below; all others work precisely the same. + + std_num_cmp($correctAnswer) OR + std_num_cmp($correctAnswer, $relPercentTol) OR + std_num_cmp($correctAnswer, $relPercentTol, $format) OR + std_num_cmp($correctAnswer, $relPercentTol, $format, $zeroLevel) OR + std_num_cmp($correctAnswer, $relPercentTol, $format, $zeroLevel, $zeroLevelTol) + + $correctAnswer -- the correct answer + $relPercentTol -- the tolerance, as a percentage (optional) + $format -- the format of the displayed answer (optional) + $zeroLevel -- if the correct answer is this close to zero, then zeroLevelTol applies (optional) + $zeroLevelTol -- absolute tolerance to allow when correct answer is close to zero (optional) + + std_num_cmp() uses standard mode (arithmetic operations and elementary + functions allowed) and relative tolerance. Options are specified by + one or more parameters. Note that if you wish to set an option which + is later in the parameter list, you must set all previous options. + + std_num_cmp_abs($correctAnswer) OR + std_num_cmp_abs($correctAnswer, $absTol) OR + std_num_cmp_abs($correctAnswer, $absTol, $format) + + $correctAnswer -- the correct answer + $absTol -- an absolute tolerance (optional) + $format -- the format of the displayed answer (optional) + + std_num_cmp_abs() uses standard mode and absolute tolerance. Options + are set as with std_num_cmp(). Note that $zeroLevel and $zeroLevelTol + do not apply with absolute tolerance. + + std_num_cmp_list($relPercentTol, $format, @answerList) + + $relPercentTol -- the tolerance, as a percentage + $format -- the format of the displayed answer(s) + @answerList -- a list of one or more correct answers + + std_num_cmp_list() uses standard mode and relative tolerance. There + is no way to set $zeroLevel or $zeroLevelTol. Note that no + parameters are optional. All answers in the list will be + evaluated with the same set of parameters. + + std_num_cmp_abs_list($absTol, $format, @answerList) + + $absTol -- an absolute tolerance + $format -- the format of the displayed answer(s) + @answerList -- a list of one or more correct answers + + std_num_cmp_abs_list() uses standard mode and absolute tolerance. + Note that no parameters are optional. All answers in the list will be + evaluated with the same set of parameters. + + arith_num_cmp(), arith_num_cmp_list(), arith_num_cmp_abs(), arith_num_cmp_abs_list() + strict_num_cmp(), strict_num_cmp_list(), strict_num_cmp_abs(), strict_num_cmp_abs_list() + frac_num_cmp(), frac_num_cmp_list(), frac_num_cmp_abs(), frac_num_cmp_abs_list() + +Examples: + + ANS( strict_num_cmp( 3.14159 ) ) -- The student answer must be a number + in decimal or scientific notation which is within .1 percent of 3.14159. + This assumes $numRelPercentTolDefault has been set to .1. + ANS( strict_num_cmp( $answer, .01 ) ) -- The student answer must be a + number within .01 percent of $answer (e.g. 3.14159 if $answer is 3.14159 + or $answer is "pi" or $answer is 4*atan(1)). + ANS( frac_num_cmp( $answer) ) or ANS( frac_num_cmp( $answer,.01 )) -- + The student answer can be a number or fraction, e.g. 2/3. + ANS( arith_num_cmp( $answer) ) or ANS( arith_num_cmp( $answer,.01 )) -- + The student answer can be an arithmetic expression, e.g. (2+3)/7-2^.5 . + ANS( std_num_cmp( $answer) ) or ANS( std_num_cmp( $answer,.01 )) -- + The student answer can contain elementary functions, e.g. sin(.3+pi/2) + +=cut + +sub std_num_cmp { # compare numbers allowing use of elementary functions + my ( $correctAnswer, $relPercentTol, $format, $zeroLevel, $zeroLevelTol ) = @_; + + my %options = ( 'relTol' => $relPercentTol, + 'format' => $format, + 'zeroLevel' => $zeroLevel, + 'zeroLevelTol' => $zeroLevelTol + ); + + set_default_options( \%options, + 'tolType' => 'relative', + 'tolerance' => $numRelPercentTolDefault, + 'mode' => 'std', + 'format' => $numFormatDefault, + 'relTol' => $numRelPercentTolDefault, + 'zeroLevel' => $numZeroLevelDefault, + 'zeroLevelTol' => $numZeroLevelTolDefault, + 'debug' => 0, + ); + + num_cmp([$correctAnswer], %options); +} + +## Similar to std_num_cmp but accepts a list of numbers in the form +## std_num_cmp_list(relpercentTol,format,ans1,ans2,ans3,...) +## format is of the form "%10.3g" or "", i.e., a format suitable for sprintf(). Use "" for default +## You must enter a format and tolerance + +sub std_num_cmp_list { + my ( $relPercentTol, $format, @answerList) = @_; + + my %options = ( 'relTol' => $relPercentTol, + 'format' => $format, + ); + + set_default_options( \%options, + 'tolType' => 'relative', + 'tolerance' => $numRelPercentTolDefault, + 'mode' => 'std', + 'format' => $numFormatDefault, + 'relTol' => $numRelPercentTolDefault, + 'zeroLevel' => $numZeroLevelDefault, + 'zeroLevelTol' => $numZeroLevelTolDefault, + 'debug' => 0, + ); + + num_cmp(\@answerList, %options); + +} + +sub std_num_cmp_abs { # compare numbers allowing use of elementary functions with absolute tolerance + my ( $correctAnswer, $absTol, $format) = @_; + my %options = ( 'tolerance' => $absTol, + 'format' => $format + ); + + set_default_options (\%options, + 'tolType' => 'absolute', + 'tolerance' => $absTol, + 'mode' => 'std', + 'format' => $numFormatDefault, + 'zeroLevel' => 0, + 'zeroLevelTol' => 0, + 'debug' => 0, + ); + + num_cmp([$correctAnswer], %options); +} + +## See std_num_cmp_list for usage + +sub std_num_cmp_abs_list { + my ( $absTol, $format, @answerList ) = @_; + + my %options = ( 'tolerance' => $absTol, + 'format' => $format, + ); + + set_default_options( \%options, + 'tolType' => 'absolute', + 'tolerance' => $absTol, + 'mode' => 'std', + 'format' => $numFormatDefault, + 'zeroLevel' => 0, + 'zeroLevelTol' => 0, + 'debug' => 0, + ); + + num_cmp(\@answerList, %options); +} + +sub frac_num_cmp { # only allow fractions and numbers as submitted answer + + my ( $correctAnswer, $relPercentTol, $format, $zeroLevel, $zeroLevelTol ) = @_; + + my %options = ( 'relTol' => $relPercentTol, + 'format' => $format, + 'zeroLevel' => $zeroLevel, + 'zeroLevelTol' => $zeroLevelTol + ); + + set_default_options( \%options, + 'tolType' => 'relative', + 'tolerance' => $relPercentTol, + 'mode' => 'frac', + 'format' => $numFormatDefault, + 'zeroLevel' => $numZeroLevelDefault, + 'zeroLevelTol' => $numZeroLevelTolDefault, + 'relTol' => $numRelPercentTolDefault, + 'debug' => 0, + ); + + num_cmp([$correctAnswer], %options); +} + +## See std_num_cmp_list for usage +sub frac_num_cmp_list { + my ( $relPercentTol, $format, @answerList ) = @_; + + my %options = ( 'relTol' => $relPercentTol, + 'format' => $format + ); + + set_default_options( \%options, + 'tolType' => 'relative', + 'tolerance' => $relPercentTol, + 'mode' => 'frac', + 'format' => $numFormatDefault, + 'zeroLevel' => $numZeroLevelDefault, + 'zeroLevelTol' => $numZeroLevelTolDefault, + 'relTol' => $numRelPercentTolDefault, + 'debug' => 0, + ); + + num_cmp(\@answerList, %options); +} + +sub frac_num_cmp_abs { # only allow fraction expressions as submitted answer with absolute tolerance + my ( $correctAnswer, $absTol, $format ) = @_; + + my %options = ( 'tolerance' => $absTol, + 'format' => $format + ); + + set_default_options (\%options, + 'tolType' => 'absolute', + 'tolerance' => $absTol, + 'mode' => 'frac', + 'format' => $numFormatDefault, + 'zeroLevel' => 0, + 'zeroLevelTol' => 0, + 'debug' => 0, + ); + + num_cmp([$correctAnswer], %options); +} + +## See std_num_cmp_list for usage + +sub frac_num_cmp_abs_list { + my ( $absTol, $format, @answerList ) = @_; + + my %options = ( 'tolerance' => $absTol, + 'format' => $format + ); + + set_default_options (\%options, + 'tolType' => 'absolute', + 'tolerance' => $absTol, + 'mode' => 'frac', + 'format' => $numFormatDefault, + 'zeroLevel' => 0, + 'zeroLevelTol' => 0, + 'debug' => 0, + ); + + num_cmp(\@answerList, %options); +} + + +sub arith_num_cmp { # only allow arithmetic expressions as submitted answer + + my ( $correctAnswer, $relPercentTol, $format, $zeroLevel, $zeroLevelTol ) = @_; + + my %options = ( 'relTol' => $relPercentTol, + 'format' => $format, + 'zeroLevel' => $zeroLevel, + 'zeroLevelTol' => $zeroLevelTol + ); + + set_default_options( \%options, + 'tolType' => 'relative', + 'tolerance' => $relPercentTol, + 'mode' => 'arith', + 'format' => $numFormatDefault, + 'zeroLevel' => $numZeroLevelDefault, + 'zeroLevelTol' => $numZeroLevelTolDefault, + 'relTol' => $numRelPercentTolDefault, + 'debug' => 0, + ); + + num_cmp([$correctAnswer], %options); +} + +## See std_num_cmp_list for usage +sub arith_num_cmp_list { + my ( $relPercentTol, $format, @answerList ) = @_; + + my %options = ( 'relTol' => $relPercentTol, + 'format' => $format, + ); + + set_default_options( \%options, + 'tolType' => 'relative', + 'tolerance' => $relPercentTol, + 'mode' => 'arith', + 'format' => $numFormatDefault, + 'zeroLevel' => $numZeroLevelDefault, + 'zeroLevelTol' => $numZeroLevelTolDefault, + 'relTol' => $numRelPercentTolDefault, + 'debug' => 0, + ); + + num_cmp(\@answerList, %options); +} + +sub arith_num_cmp_abs { # only allow arithmetic expressions as submitted answer with absolute tolerance + my ( $correctAnswer, $absTol, $format ) = @_; + + my %options = ( 'tolerance' => $absTol, + 'format' => $format + ); + + set_default_options (\%options, + 'tolType' => 'absolute', + 'tolerance' => $absTol, + 'mode' => 'arith', + 'format' => $numFormatDefault, + 'zeroLevel' => 0, + 'zeroLevelTol' => 0, + 'debug' => 0, + ); + + num_cmp([$correctAnswer], %options); +} + +## See std_num_cmp_list for usage +sub arith_num_cmp_abs_list { + my ( $absTol, $format, @answerList ) = @_; + + my %options = ( 'tolerance' => $absTol, + 'format' => $format + ); + + set_default_options (\%options, + 'tolType' => 'absolute', + 'tolerance' => $absTol, + 'mode' => 'arith', + 'format' => $numFormatDefault, + 'zeroLevel' => 0, + 'zeroLevelTol' => 0, + 'debug' => 0, + ); + + num_cmp(\@answerList, %options); +} + +sub strict_num_cmp { # only allow numbers as submitted answer + my ( $correctAnswer, $relPercentTol, $format, $zeroLevel, $zeroLevelTol ) = @_; + + my %options = ( 'relTol' => $relPercentTol, + 'format' => $format, + 'zeroLevel' => $zeroLevel, + 'zeroLevelTol' => $zeroLevelTol + ); + + set_default_options( \%options, + 'tolType' => 'relative', + 'tolerance' => $relPercentTol, + 'mode' => 'strict', + 'format' => $numFormatDefault, + 'zeroLevel' => $numZeroLevelDefault, + 'zeroLevelTol' => $numZeroLevelTolDefault, + 'relTol' => $numRelPercentTolDefault, + 'debug' => 0, + ); + num_cmp([$correctAnswer], %options); + +} + +## See std_num_cmp_list for usage +sub strict_num_cmp_list { # compare numbers + my ( $relPercentTol, $format, @answerList ) = @_; + + my %options = ( 'relTol' => $relPercentTol, + 'format' => $format, + ); + + set_default_options( \%options, + 'tolType' => 'relative', + 'tolerance' => $relPercentTol, + 'mode' => 'strict', + 'format' => $numFormatDefault, + 'zeroLevel' => $numZeroLevelDefault, + 'zeroLevelTol' => $numZeroLevelTolDefault, + 'relTol' => $numRelPercentTolDefault, + 'debug' => 0, + ); + + num_cmp(\@answerList, %options); +} + + +sub strict_num_cmp_abs { # only allow numbers as submitted answer with absolute tolerance + my ( $correctAnswer, $absTol, $format ) = @_; + + my %options = ( 'tolerance' => $absTol, + 'format' => $format + ); + + set_default_options (\%options, + 'tolType' => 'absolute', + 'tolerance' => $absTol, + 'mode' => 'strict', + 'format' => $numFormatDefault, + 'zeroLevel' => 0, + 'zeroLevelTol' => 0, + 'debug' => 0, + ); + num_cmp([$correctAnswer], %options); + +} + +## See std_num_cmp_list for usage +sub strict_num_cmp_abs_list { # compare numbers + my ( $absTol, $format, @answerList ) = @_; + + my %options = ( 'tolerance' => $absTol, + 'format' => $format + ); + + set_default_options (\%options, + 'tolType' => 'absolute', + 'tolerance' => $absTol, + 'mode' => 'strict', + 'format' => $numFormatDefault, + 'zeroLevel' => 0, + 'zeroLevelTol' => 0, + 'debug' => 0, + ); + + num_cmp(\@answerList, %options); +} + +## sub numerical_compare_with_units +## Compares a number with units +## Deprecated; use num_cmp() +## +## IN: a string which includes the numerical answer and the units +## a hash with the following keys (all optional): +## mode -- 'std', 'frac', 'arith', or 'strict' +## format -- the format to use when displaying the answer +## tol -- an absolute tolerance, or +## relTol -- a relative tolerance +## zeroLevel -- if the correct answer is this close to zero, then zeroLevelTol applies +## zeroLevelTol -- absolute tolerance to allow when correct answer is close to zero + +# This mode is depricated. send input through num_cmp -- it can handle units. + +sub numerical_compare_with_units { + my $correct_answer = shift; # the answer is a string which includes both the numerical answer and the units. + my %options = @_; # all of the other inputs are (key value) pairs + + # Prepare the correct answer + $correct_answer = str_filters( $correct_answer, 'trim_whitespace' ); + + # it surprises me that the match below works since the first .* is greedy. + my ($correct_num_answer, $correct_units) = $correct_answer =~ /^(.*)\s+([^\s]*)$/; + $options{units} = $correct_units; + + num_cmp($correct_num_answer, %options); +} + + +=head3 std_num_str_cmp() + +NOTE: This function is maintained for compatibility. num_cmp() with the + 'strings' parameter is slightly preferred. + +std_num_str_cmp() is used when the correct answer could be either a number or a +string. For example, if you wanted the student to evaluate a function at number +of points, but write "Inf" or "Minf" if the function is unbounded. This routine +will provide error messages that do not give a hint as to whether the correct +answer is a string or a number. For numerical comparisons, std_num_cmp() is +used internally; for string comparisons, std_str_cmp() is used. String answers +must consist entirely of letters except that an initial minus sign is allowed. +E.g. "inf" and "-inf" are valid strings where as "too-big" is not. + + std_num_str_cmp( $correctAnswer ) OR + std_num_str_cmp( $correctAnswer, $ra_legalStrings ) OR + std_num_str_cmp( $correctAnswer, $ra_legalStrings, $relPercentTol ) OR + std_num_str_cmp( $correctAnswer, $ra_legalStrings, $relPercentTol, $format ) OR + std_num_str_cmp( $correctAnswer, $ra_legalStrings, $relPercentTol, $format, $zeroLevel ) OR + std_num_str_cmp( $correctAnswer, $ra_legalStrings, $relPercentTol, $format, + $zeroLevel, $zeroLevelTol ) + + $correctAnswer -- the correct answer + $ra_legalStrings -- a reference to an array of legal strings, e.g. ["str1", "str2"] + $relPercentTol -- the error tolerance as a percentage + $format -- the display format + $zeroLevel -- if the correct answer is this close to zero, then zeroLevelTol applies + $zeroLevelTol -- absolute tolerance to allow when correct answer is close to zero + +Examples: + ANS( std_num_str_cmp( $ans, ["Inf", "Minf", "NaN"] ) ); + ANS( std_num_str_cmp( $ans, ["INF", "-INF"] ) ); + +=cut + +sub std_num_str_cmp { + my ( $correctAnswer, $ra_legalStrings, $relpercentTol, $format, $zeroLevel, $zeroLevelTol ) = @_; + # warn ('This method is depreciated. Use num_cmp instead.'); + return num_cmp ($correctAnswer, strings=>$ra_legalStrings, relTol=>$relpercentTol, format=>$format, + zeroLevel=>$zeroLevel, zeroLevelTol=>$zeroLevelTol); +} + +sub NUM_CMP { # low level numeric compare (now uses Parser) + return ORIGINAL_NUM_CMP(@_) + if main::PG_restricted_eval(q!$main::useOldAnswerMacros!); + + my %num_params = @_; + + # + # check for required parameters + # + my @keys = qw(correctAnswer tolerance tolType format mode zeroLevel zeroLevelTol debug); + foreach my $key (@keys) { + warn "$key must be defined in options when calling NUM_CMP" + unless defined($num_params{$key}); + } + + my $correctAnswer = $num_params{correctAnswer}; + my $mode = $num_params{mode}; + my %options = (debug => $num_params{debug}); + + # + # Get an apppropriate context based on the mode + # + my $context; + for ($mode) { + /^strict$/i and do { + $context = &$Context("LimitedNumeric")->copy; + last; + }; + /^arith$/i and do { + $context = &$Context("Numeric")->copy; + $context->functions->disable('All'); + last; + }; + /^frac$/i and do { + $context = &$Context("LimitedNumeric-Fraction")->copy; + last; + }; + + # default + $context = &$Context("Numeric")->copy; + } + $context->{format}{number} = $num_params{'format'}; + $context->strings->clear; + # FIXME: should clear variables as well? Copy them from the current context? + + # + # Add the strings to the context + # + if (defined($num_params{strings}) && $num_params{strings}) { + foreach my $string (@{$num_params{strings}}) { + my %tex = ($string =~ m/(-?)inf(inity)?/i)? (TeX => "$1\\infty"): (); + $context->strings->add(uc($string) => {%tex}); + } + } + + # + # Set the tolerances + # + if ($num_params{tolType} eq 'relative') { + $context->flags->set( + tolerance => .01*$num_params{tolerance}, + tolType => 'relative', + ); + } else { + $context->flags->set( + tolerance => $num_params{tolerance}, + tolType => 'absolute', + ); + } + $context->flags->set( + zeroLevel => $num_params{zeroLevel}, + zeroLevelTol => $num_params{zeroLevelTol}, + ); + + # + # Get the proper Parser object for the professor's answer + # using the initialized context + # + my $oldContext = &$Context($context); my $r; + if (defined($num_params{units}) && $num_params{units}) { + $r = new Parser::Legacy::NumberWithUnits($correctAnswer); + $options{rh_correct_units} = $num_params{units}; + } else { + $r = Value::Formula->new($correctAnswer); + die "The professor's answer can't be a formula" unless $r->isConstant; + $r = $r->eval; $r = new Value::Real($r) unless Value::class($r) eq 'String'; + $r->{correct_ans} = $correctAnswer; + if ($mode eq 'phase_pi') { + my $pi = 4*atan2(1,1); + while ($r > $pi/2) {$r -= $pi} + while ($r < -$pi/2) {$r += $pi} + } + } + # + # Get the answer checker from the parser object + # + my $cmp = $r->cmp(%options); + $cmp->install_pre_filter(sub { + my $rh_ans = shift; + $rh_ans->{original_student_ans} = $rh_ans->{student_ans}; + $rh_ans->{original_correct_ans} = $rh_ans->{correct_ans}; + return $rh_ans; + }); + $cmp->{debug} = $num_params{debug}; + &$Context($oldContext); + + return $cmp; +} + +# +# The original version, for backward compatibility +# (can be removed when the Parser-based version is more fully tested.) +# +sub ORIGINAL_NUM_CMP { # low level numeric compare + my %num_params = @_; + + my @keys = qw ( correctAnswer tolerance tolType format mode zeroLevel zeroLevelTol debug ); + foreach my $key (@keys) { + warn "$key must be defined in options when calling NUM_CMP" unless defined ($num_params{$key}); + } + + my $correctAnswer = $num_params{'correctAnswer'}; + my $format = $num_params{'format'}; + my $mode = $num_params{'mode'}; + + if( $num_params{tolType} eq 'relative' ) { + $num_params{'tolerance'} = .01*$num_params{'tolerance'}; + } + + my $formattedCorrectAnswer; + my $correct_units; + my $correct_num_answer; + my %correct_units; + my $corrAnswerIsString = 0; + + + if (defined($num_params{units}) && $num_params{units}) { + $correctAnswer = str_filters( $correctAnswer, 'trim_whitespace' ); + # units are in form stuff space units where units contains no spaces. + + ($correct_num_answer, $correct_units) = $correctAnswer =~ /^(.*)\s+([^\s]*)$/; + %correct_units = Units::evaluate_units($correct_units); + if ( defined( $correct_units{'ERROR'} ) ) { + warn ("ERROR: The answer \"$correctAnswer\" in the problem definition cannot be parsed:\n" . + "$correct_units{'ERROR'}\n"); + } + # $formattedCorrectAnswer = spf($correct_num_answer,$num_params{'format'}) . " $correct_units"; + $formattedCorrectAnswer = prfmt($correct_num_answer,$num_params{'format'}) . " $correct_units"; + + } elsif (defined($num_params{strings}) && $num_params{strings}) { + my $legalString = ''; + my @legalStrings = @{$num_params{strings}}; + $correct_num_answer = $correctAnswer; + $formattedCorrectAnswer = $correctAnswer; + foreach $legalString (@legalStrings) { + if ( uc($correctAnswer) eq uc($legalString) ) { + $corrAnswerIsString = 1; + + last; + } + } ## at this point $corrAnswerIsString = 0 iff correct answer is numeric + } else { + $correct_num_answer = $correctAnswer; + $formattedCorrectAnswer = prfmt( $correctAnswer, $num_params{'format'} ); + } + + $correct_num_answer = math_constants($correct_num_answer); + + my $PGanswerMessage = ''; + + my ($inVal,$correctVal,$PG_eval_errors,$PG_full_error_report); + + if (defined($correct_num_answer) && $correct_num_answer =~ /\S/ && $corrAnswerIsString == 0 ) { + ($correctVal, $PG_eval_errors,$PG_full_error_report) = PG_answer_eval($correct_num_answer); + } else { # case of a string answer + $PG_eval_errors = ' '; + $correctVal = $correctAnswer; + } + + if ( ($PG_eval_errors && $corrAnswerIsString == 0) or ((not is_a_number($correctVal)) && $corrAnswerIsString == 0)) { + ##error message from eval or above + warn "Error in 'correct' answer: $PG_eval_errors<br> + The answer $correctAnswer evaluates to $correctVal, + which cannot be interpreted as a number. "; + + } + ######################################################################### + + #construct the answer evaluator + my $answer_evaluator = new AnswerEvaluator; + $answer_evaluator->{debug} = $num_params{debug}; + $answer_evaluator->ans_hash( + correct_ans => $correctVal, + type => "${mode}_number", + tolerance => $num_params{tolerance}, + tolType => $num_params{tolType}, + units => $correct_units, + original_correct_ans => $formattedCorrectAnswer, + rh_correct_units => \%correct_units, + answerIsString => $corrAnswerIsString, + ); + my ($in, $formattedSubmittedAnswer); + $answer_evaluator->install_pre_filter(sub {my $rh_ans = shift; + $rh_ans->{original_student_ans} = $rh_ans->{student_ans}; $rh_ans;} + ); + + + + if (defined($num_params{units}) && $num_params{units}) { + $answer_evaluator->install_pre_filter(\&check_units); + } + if (defined($num_params{strings}) && $num_params{strings}) { + $answer_evaluator->install_pre_filter(\&check_strings, %num_params); + } + + ## FIXME? - this pre filter was moved before check_units to allow + ## for latex preview of answers with no units. + ## seems to work but may have unintended side effects elsewhere. + + ## Actually it caused trouble with the check strings package so it has been moved back + # We'll try some other method -- perhaps add code to fix_answer for display + $answer_evaluator->install_pre_filter(\&check_syntax); + + $answer_evaluator->install_pre_filter(\&math_constants); + + if ($mode eq 'std') { + # do nothing + } elsif ($mode eq 'strict') { + $answer_evaluator->install_pre_filter(\&is_a_number); + } elsif ($mode eq 'arith') { + $answer_evaluator->install_pre_filter(\&is_an_arithmetic_expression); + } elsif ($mode eq 'frac') { + $answer_evaluator->install_pre_filter(\&is_a_fraction); + + } elsif ($mode eq 'phase_pi') { + $answer_evaluator->install_pre_filter(\&phase_pi); + + } else { + $PGanswerMessage = 'Tell your professor that there is an error in his or her answer mechanism. No mode was specified.'; + $formattedSubmittedAnswer = $in; + } + + if ($corrAnswerIsString == 0 ){ # avoiding running compare_numbers when correct answer is a string. + $answer_evaluator->install_evaluator(\&compare_numbers, %num_params); + } + + +############################################################################### +# We'll leave these next lines out for now, so that the evaluated versions of the student's and professor's +# can be displayed in the answer message. This may still cause a few anomolies when strings are used +# +############################################################################### + + $answer_evaluator->install_post_filter(\&fix_answers_for_display); + + $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; + return $rh_ans unless $rh_ans->catch_error('EVAL'); + $rh_ans->{student_ans} = $rh_ans->{original_student_ans}. ' '. $rh_ans->{error_message}; + $rh_ans->clear_error('EVAL'); } ); + $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('SYNTAX'); } ); + $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('UNITS'); } ); + $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('NUMBER'); } ); + $answer_evaluator->install_post_filter(sub {my $rh_ans = shift; $rh_ans->clear_error('STRING'); } ); + $answer_evaluator; +} + + + +########################################################################## +########################################################################## +## Function answer evaluators + +=head2 Function Answer Evaluators + +Function answer evaluators take in a function, compare it numerically to a +correct function, and return a score. They can require an exactly equivalent +function, or one that is equal up to a constant. They can accept or reject an +answer based on specified tolerances for numerical deviation. + +Function Comparison Options + + correctEqn -- The correct equation, specified as a string. It may include + all basic arithmetic operations, as well as elementary + functions. Variable usage is described below. + + Variables -- The independent variable(s). When comparing the correct + equation to the student equation, each variable will be + replaced by a certain number of numerical values. If + the student equation agrees numerically with the correct + equation, they are considered equal. Note that all + comparison is numeric; it is possible (although highly + unlikely and never a practical concern) for two unequal + functions to yield the same numerical results. + + Limits -- The limits of evaluation for the independent variables. + Each variable is evaluated only in the half-open interval + [lower_limit, upper_limit). This is useful if the function + has a singularity or is not defined in a certain range. + For example, the function "sqrt(-1-x)" could be evaluated + in [-2,-1). + + Tolerance -- Tolerance in function comparisons works exactly as in + numerical comparisons; see the numerical comparison + documentation for a complete description. Note that the + tolerance does applies to the function as a whole, not + each point individually. + + Number of -- Specifies how many points to evaluate each variable at. This + Points is typically 3, but can be set higher if it is felt that + there is a strong possibility of "false positives." + + Maximum -- Sets the maximum size of the constant of integration. For + Constant of technical reasons concerning floating point arithmetic, if + Integration the additive constant, i.e., the constant of integration, is + greater (in absolute value) than maxConstantOfIntegration + AND is greater than maxConstantOfIntegration times the + correct value, WeBWorK will give an error message saying + that it can not handle such a large constant of integration. + This is to prevent e.g. cos(x) + 1E20 or even 1E20 as being + accepted as a correct antiderivatives of sin(x) since + floating point arithmetic cannot tell the difference + between cos(x) + 1E20, 1E20, and -cos(x) + 1E20. + +Technical note: if you examine the code for the function routines, you will see +that most subroutines are simply doing some basic error-checking and then +passing the parameters on to the low-level FUNCTION_CMP(). Because this routine +is set up to handle multivariable functions, with single-variable functions as +a special case, it is possible to pass multivariable parameters to single- +variable functions. This usage is strongly discouraged as unnecessarily +confusing. Avoid it. + +Default Values (As of 7/24/2000) (Option -- Variable Name -- Value) + + Variable -- $functVarDefault -- 'x' + Relative Tolerance -- $functRelPercentTolDefault -- .1 + Absolute Tolerance -- $functAbsTolDefault -- .001 + Lower Limit -- $functLLimitDefault -- .0000001 + Upper Limit -- $functULimitDefault -- 1 + Number of Points -- $functNumOfPoints -- 3 + Zero Level -- $functZeroLevelDefault -- 1E-14 + Zero Level Tolerance -- $functZeroLevelTolDefault -- 1E-12 + Maximum Constant -- $functMaxConstantOfIntegration -- 1E8 + of Integration + +=cut + + + +=head3 fun_cmp() + +Compares a function or a list of functions, using a named hash of options to set +parameters. This can make for more readable code than using the function_cmp() +style, but some people find one or the other easier to remember. + +ANS( fun_cmp( answer or answer_array_ref, options_hash ) ); + + 1. a string containing the correct function, or a reference to an + array of correct functions + 2. a hash containing the following items (all optional): + var -- either the number of variables or a reference to an + array of variable names (see below) + limits -- reference to an array of arrays of limits (see below), or: + mode -- 'std' (default) (function must match exactly), or: + 'antider' (function must match up to a constant) + relTol -- (default) a relative tolerance (as a percentage), or: + tol -- an absolute tolerance for error + numPoints -- the number of points to evaluate the function at + maxConstantOfIntegration -- maximum size of the constant of integration + zeroLevel -- if the correct answer is this close to zero, then + zeroLevelTol applies + zeroLevelTol -- absolute tolerance to allow when answer is close to zero + test_points -- a list of points to use in checking the function, or a list of lists when there is more than one variable. + params an array of "free" parameters which can be used to adapt + the correct answer to the submitted answer. (e.g. ['c'] for + a constant of in... [truncated message content] |
From: dpvc v. a. <we...@ma...> - 2005-07-04 20:11:26
|
Update of /webwork/cvs/system/pg/lib/Parser/Legacy In directory devel.webwork.rochester.edu:/tmp/cvs-serv17747/Legacy Log Message: Directory /webwork/cvs/system/pg/lib/Parser/Legacy added to the repository |
From: Mike G. v. a. <we...@ma...> - 2005-07-04 19:55:39
|
Log Message: ----------- Rewrote checkbox_cmp as an AnswerEvaluator Modified Files: -------------- pg/macros: PGanswermacros.pl Revision Data ------------- Index: PGanswermacros.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/PGanswermacros.pl,v retrieving revision 1.32 retrieving revision 1.33 diff -Lmacros/PGanswermacros.pl -Lmacros/PGanswermacros.pl -u -r1.32 -r1.33 --- macros/PGanswermacros.pl +++ macros/PGanswermacros.pl @@ -2173,6 +2173,7 @@ sub ignore_order { my $rh_ans = shift; die "expected an answer hash" unless ref($rh_ans)=~/HASH/i; + $rh_ans->{_filter_name} = 'ignore_order'; $rh_ans->{student_ans} = join( "", lex_sort( split( /\s*/, $rh_ans->{student_ans} ) ) ); $rh_ans->{correct_ans} = join( "", lex_sort( split( /\s*/, $rh_ans->{correct_ans} ) ) ); @@ -2617,40 +2618,117 @@ # rather than as a \0 delimited string. sub checkbox_cmp { my $correctAnswer = shift @_; - $correctAnswer = str_filters( $correctAnswer, 'ignore_order' ); - - my $answer_evaluator = sub { - my $in = shift @_; - $in = '' unless defined $in; #in case no boxes checked - # multiple answers could come in two forms - # either a \0 delimited string or - # an array reference. We handle both. - if (ref($in) eq 'ARRAY') { - $in = join("",@{$in}); # convert array to single no-delimiter string - } else { - my @temp = split( "\0", $in ); #convert "\0"-delimited string to array... - $in = join( "", @temp ); #and then to a single no-delimiter string - } - my $original_student_ans = $in; #well, almost original - $in = str_filters( $in, 'ignore_order' ); - - my $correctQ = ($in eq $correctAnswer) ? 1: 0; - - my $ans_hash = new AnswerHash( - 'score' => $correctQ, - 'correct_ans' => "$correctAnswer", - 'student_ans' => $in, - 'ans_message' => "", - 'type' => "checkbox_cmp", - 'preview_text_string' => $in, - 'preview_latex_string' => $in, - 'original_student_ans' => $original_student_ans - ); - return $ans_hash; - - }; + my %options = @_; + assign_option_aliases( \%options, + ); + set_default_options( \%options, + 'debug' => 0, + 'type' => 'checkbox_cmp', + ); + my $answer_evaluator = new AnswerEvaluator( + correct_ans => $correctAnswer, + type => $options{type}, + ); + # pass along debug requests + $answer_evaluator->{debug} = $options{debug}; + + # join student answer array into a single string if necessary + $answer_evaluator->install_pre_filter(sub { + my $rh_ans = shift; + $rh_ans->{_filter_name} = 'convert student_ans to string'; + $rh_ans->{student_ans} = join("", @{$rh_ans->{student_ans}}) + if ref($rh_ans->{student_ans}) =~/ARRAY/i; + $rh_ans; + }); + # ignore order of check boxes + $answer_evaluator->install_pre_filter(\&ignore_order); + # compare as strings + $answer_evaluator->install_evaluator(sub { + my $rh_ans = shift; + $rh_ans->{_filter_name} = 'compare strings generated by checked boxes'; + $rh_ans->{score} = ($rh_ans->{student_ans} eq $rh_ans->{correct_ans}) ? 1 : 0; + $rh_ans; + }); + # fix up preview displays + $answer_evaluator->install_post_filter( sub { + my $rh_ans = shift; + $rh_ans->{_filter_name} = 'adjust preview strings'; + $rh_ans->{type} = $options{type}; + $rh_ans->{preview_text_string} = '\\text{'.$rh_ans->{student_ans}.'}', + $rh_ans->{preview_latex_string} = '\\text{'.$rh_ans->{student_ans}.'}', + $rh_ans; + + + }); + +# my $answer_evaluator = sub { +# my $in = shift @_; +# $in = '' unless defined $in; #in case no boxes checked +# # multiple answers could come in two forms +# # either a \0 delimited string or +# # an array reference. We handle both. +# if (ref($in) eq 'ARRAY') { +# $in = join("",@{$in}); # convert array to single no-delimiter string +# } else { +# my @temp = split( "\0", $in ); #convert "\0"-delimited string to array... +# $in = join( "", @temp ); #and then to a single no-delimiter string +# } +# my $original_student_ans = $in; #well, almost original +# $in = str_filters( $in, 'ignore_order' ); +# +# my $correctQ = ($in eq $correctAnswer) ? 1: 0; +# +# my $ans_hash = new AnswerHash( +# 'score' => $correctQ, +# 'correct_ans' => "$correctAnswer", +# 'student_ans' => $in, +# 'ans_message' => "", +# 'type' => "checkbox_cmp", +# 'preview_text_string' => $in, +# 'preview_latex_string' => $in, +# 'original_student_ans' => $original_student_ans +# ); +# return $ans_hash; +# +# }; return $answer_evaluator; } +# sub checkbox_cmp { +# my $correctAnswer = shift @_; +# $correctAnswer = str_filters( $correctAnswer, 'ignore_order' ); +# +# my $answer_evaluator = sub { +# my $in = shift @_; +# $in = '' unless defined $in; #in case no boxes checked +# # multiple answers could come in two forms +# # either a \0 delimited string or +# # an array reference. We handle both. +# if (ref($in) eq 'ARRAY') { +# $in = join("",@{$in}); # convert array to single no-delimiter string +# } else { +# my @temp = split( "\0", $in ); #convert "\0"-delimited string to array... +# $in = join( "", @temp ); #and then to a single no-delimiter string +# } +# my $original_student_ans = $in; #well, almost original +# $in = str_filters( $in, 'ignore_order' ); +# +# my $correctQ = ($in eq $correctAnswer) ? 1: 0; +# +# my $ans_hash = new AnswerHash( +# 'score' => $correctQ, +# 'correct_ans' => "$correctAnswer", +# 'student_ans' => $in, +# 'ans_message' => "", +# 'type' => "checkbox_cmp", +# 'preview_text_string' => $in, +# 'preview_latex_string' => $in, +# 'original_student_ans' => $original_student_ans +# ); +# return $ans_hash; +# +# }; +# return $answer_evaluator; +# } #added 6/28/2000 by David Etlinger #exactly the same as strict_str_cmp, |
From: Mike G. v. a. <we...@ma...> - 2005-07-04 19:52:16
|
Log Message: ----------- Reworked some of the code for AnswerEvaluator The messages printed out when using the debug flag should now be somewhat better. -- Mike Modified Files: -------------- pg/lib: AnswerHash.pm Revision Data ------------- Index: AnswerHash.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/AnswerHash.pm,v retrieving revision 1.7 retrieving revision 1.8 diff -Llib/AnswerHash.pm -Llib/AnswerHash.pm -u -r1.7 -r1.8 --- lib/AnswerHash.pm +++ lib/AnswerHash.pm @@ -479,6 +479,7 @@ sub dereference_array_ans { my $self = shift; my $rh_ans = shift; + $rh_ans->{_filter_name} = 'dereference_array_ans'; if (defined($rh_ans->{student_ans}) and ref($rh_ans->{student_ans}) eq 'ARRAY' ) { $rh_ans->{student_ans} = "( ". join(" , ",@{$rh_ans->{student_ans}} ) . " ) "; } @@ -512,120 +513,119 @@ $self-> {rh_ans} -> {student_ans} = $input; } $self->{rh_ans}->{ans_label} = $answer_options{ans_label} if defined($answer_options{ans_label}); - + $self->{rh_ans}->{_filter_name} = 'get_student_answer'; $input; } =head4 evaluate - + $answer_evaluator->evaluate($student_answer_string =cut +our $count; # used to keep track of where we are in queue sub evaluate { my $self = shift; $self->get_student_answer(@_); - $self->{rh_ans}->{error_flag}=undef; #reset the error flags in case - $self->{rh_ans}->{done}=undef; #the answer evaluator is called twice + # dereference $self->{rh_ans}; my $rh_ans = $self ->{rh_ans}; + $rh_ans->{error_flag}=undef; #reset the error flags in case + $rh_ans->{done}=undef; #the answer evaluator is called twice + warn "<H3> Answer evaluator information: </H3>\n" if defined($self->{debug}) and $self->{debug}>0; + $self->print_result_if_debug('pre_filter',$rh_ans); + my @prefilters = @{$self -> {pre_filters}}; - my $count = -1; # the blank filter is counted as filter 0 + $count = 0; # the get student answer filter is counted as filter -1 foreach my $i (@prefilters) { - last if defined( $self->{rh_ans}->{error_flag} ); + last if defined( $rh_ans->{error_flag} ); my @array = @$i; my $filter = shift(@array); # the array now contains the options for the filter - my %options = @array; - if (defined($self->{debug}) and $self->{debug}>0) { - - $self->{rh_ans}->{rh_options} = \%options; #include the options in the debug information - warn "before pre-filter: ",++$count, $self->{rh_ans}->pretty_print(); - } - $rh_ans = &$filter($rh_ans,@array); - warn "<h4>Filter Name:", $rh_ans->{_filter_name},"</h4><BR>\n" - if defined($self->{debug}) and $self->{debug}>0 and defined($rh_ans->{_filter_name}); - $rh_ans->{_filter_name} = undef; + $rh_ans = &$filter($rh_ans,@array); + $self->print_result_if_debug('pre_filter',$rh_ans,@array); } my @evaluators = @{$self -> {evaluators} }; $count = 0; foreach my $i ( @evaluators ) { - last if defined($self->{rh_ans}->{error_flag}); + last if defined($rh_ans->{error_flag}); my @array = @$i; my $evaluator = shift(@array); # the array now contains the options for the filter - my %options = @array; - if (defined($self->{debug}) and $self->{debug}>0) { - $self->{rh_ans}->{rh_options} = \%options; #include the options in the debug information - warn "before evaluator: ",++$count, $self->{rh_ans}->pretty_print(); - } - $rh_ans = &$evaluator($rh_ans,@array); - warn "<h4>Filter Name:", $rh_ans->{_filter_name},"</h4><BR>\n" if defined($self->{debug}) and $self->{debug}>0 and defined($rh_ans->{_filter_name}); - $rh_ans->{_filter_name} = undef; + $rh_ans = &$evaluator($rh_ans,@array); + $self->print_result_if_debug('evaluator',$rh_ans,@array); } my @post_filters = @{$self -> {post_filters} }; - $count = -1; # blank filter catcher is filter 0 + $count = 0; # blank filter catcher is filter 0 foreach my $i ( @post_filters ) { last if defined($rh_ans->{done}) and $rh_ans->{done} == 1; # no further action needed my @array = @$i; my $filter = shift(@array); # the array now contains the options for the filter - my %options = @array; - if (defined($self->{debug}) and $self->{debug}>0) { - $self->{rh_ans}->{rh_options} = \%options; #include the options in the debug information - warn "before post-filter: ",++$count, $self->{rh_ans}->pretty_print(),"\n"; - } - - $rh_ans = &$filter($rh_ans,@array); - warn "<h4>Filter Name:", $rh_ans->{_filter_name},"</h4><BR>\n" if defined($self->{debug}) and $self->{debug}>0 and defined($rh_ans->{_filter_name}); - $rh_ans->{_filter_name} = undef; + $rh_ans = &$filter($rh_ans,@array); + $self->print_result_if_debug('post_filter',$rh_ans,@array); } $rh_ans = $self->dereference_array_ans($rh_ans); # make sure that the student answer is not an array so that it is reported correctly in answer section. - warn "<h4>final result: </h4>", $self->{rh_ans}->pretty_print() if defined($self->{debug}) and $self->{debug}>0; + warn "<h4>final result: </h4>", $rh_ans->pretty_print() if defined($self->{debug}) and $self->{debug}>0; + # re-refrence $rh_ans; $self ->{rh_ans} = $rh_ans; $rh_ans; } -# This next subroutine is for checking the instructor's answer and is not yet in use. -sub correct_answer_evaluate { - my $self = shift; - $self-> {rh_ans} -> {correct_ans} = shift @_; - my $rh_ans = $self ->{rh_ans}; - my @prefilters = @{$self -> {correct_answer_pre_filters}}; - my $count = -1; # the blank filter is counted as filter 0 - foreach my $i (@prefilters) { - last if defined( $self->{rh_ans}->{error_flag} ); - my @array = @$i; - my $filter = shift(@array); # the array now contains the options for the filter - warn "before pre-filter: ",++$count, $self->{rh_ans}->pretty_print() if defined($self->{debug}) and $self->{debug}>0; - $rh_ans = &$filter($rh_ans,@array); - warn "Filter Name:", $rh_ans->{_filter_name},"<BR>\n" if $self->{debug}>0 and defined($rh_ans->{_filter_name}) - } - my @evaluators = @{$self -> {correct_answer_evaluators} }; - $count = 0; - foreach my $i ( @evaluators ) { - last if defined($self->{rh_ans}->{error_flag}); - my @array = @$i; - my $evaluator = shift(@array); # the array now contains the options for the filter - warn "before evaluator: ",++$count, $self->{rh_ans}->pretty_print() if defined($self->{debug}) and $self->{debug}>0; - $rh_ans = &$evaluator($rh_ans,@array); - } - my @post_filters = @{$self -> {correct_answer_post_filters} }; - $count = -1; # blank filter catcher is filter 0 - foreach my $i ( @post_filters ) { - last if defined($rh_ans->{done}) and $rh_ans->{done} == 1; # no further action needed - my @array = @$i; - my $filter = shift(@array); # the array now contains the options for the filter - warn "before post-filter: ",++$count, $self->{rh_ans}->pretty_print() if defined($self->{debug}) and $self->{debug}>0; - $rh_ans = &$filter($rh_ans,@array); - warn "Filter Name:", $rh_ans->{_filter_name},"<BR>\n" if $self->{debug}>0 and defined($rh_ans->{_filter_name}) - } - $rh_ans = $self->dereference_array_ans($rh_ans); - # make sure that the student answer is not an array so that it is reported correctly in answer section. - warn "final result: ", $self->{rh_ans}->pretty_print() if defined($self->{debug}) and $self->{debug}>0; - $self ->{rh_ans} = $rh_ans; - $rh_ans; +sub print_result_if_debug { + my $self = shift; + my $queue = shift; # the name of the queue we are in + my $rh_ans= shift; + my %options = @_; + if (defined($self->{debug}) and $self->{debug}>0) { + $rh_ans->{rh_options} = \%options; #include the options in the debug information + my $name = (defined($rh_ans->{_filter_name})) ? $rh_ans->{_filter_name}: 'unnamed'; + warn "$count. Result from \"$name\" $queue:", $rh_ans->pretty_print(); + ++$count; + } + $rh_ans->{_filter_name} = undef; } +# This next subroutine is for checking the instructor's answer and is not yet in use. +# sub correct_answer_evaluate { +# my $self = shift; +# $self-> {rh_ans} -> {correct_ans} = shift @_; +# my $rh_ans = $self ->{rh_ans}; +# my @prefilters = @{$self -> {correct_answer_pre_filters}}; +# my $count = -1; # the blank filter is counted as filter 0 +# foreach my $i (@prefilters) { +# last if defined( $rh_ans->{error_flag} ); +# my @array = @$i; +# my $filter = shift(@array); # the array now contains the options for the filter +# warn "before pre-filter: ",++$count, $self->{rh_ans}->pretty_print() if defined($self->{debug}) and $self->{debug}>0; +# $rh_ans = &$filter($rh_ans,@array); +# warn "Filter Name:", $rh_ans->{_filter_name},"<BR>\n" if $self->{debug}>0 and defined($rh_ans->{_filter_name}) +# } +# my @evaluators = @{$self -> {correct_answer_evaluators} }; +# $count = 0; +# foreach my $i ( @evaluators ) { +# last if defined($self->{rh_ans}->{error_flag}); +# my @array = @$i; +# my $evaluator = shift(@array); # the array now contains the options for the filter +# warn "before evaluator: ",++$count, $self->{rh_ans}->pretty_print() if defined($self->{debug}) and $self->{debug}>0; +# $rh_ans = &$evaluator($rh_ans,@array); +# } +# my @post_filters = @{$self -> {correct_answer_post_filters} }; +# $count = -1; # blank filter catcher is filter 0 +# foreach my $i ( @post_filters ) { +# last if defined($rh_ans->{done}) and $rh_ans->{done} == 1; # no further action needed +# my @array = @$i; +# my $filter = shift(@array); # the array now contains the options for the filter +# warn "before post-filter: ",++$count, $self->{rh_ans}->pretty_print() if defined($self->{debug}) and $self->{debug}>0; +# $rh_ans = &$filter($rh_ans,@array); +# warn "Filter Name:", $rh_ans->{_filter_name},"<BR>\n" if $self->{debug}>0 and defined($rh_ans->{_filter_name}) +# } +# $rh_ans = $self->dereference_array_ans($rh_ans); +# # make sure that the student answer is not an array so that it is reported correctly in answer section. +# warn "final result: ", $self->{rh_ans}->pretty_print() if defined($self->{debug}) and $self->{debug}>0; +# $self ->{rh_ans} = $rh_ans; +# $rh_ans; +# } + =head4 install_pre_filter @@ -641,7 +641,6 @@ =cut - sub install_pre_filter { my $self = shift; if (@_ == 0) { @@ -780,6 +779,7 @@ sub blank_prefilter { # check for blanks my $rh_ans = shift; + $rh_ans->{_filter_name} = 'blank_prefilter'; # undefined answers are BLANKS ( not defined($rh_ans->{student_ans}) ) && do {$rh_ans->throw_error("BLANK", 'The answer is blank'); return($rh_ans);}; @@ -795,6 +795,7 @@ sub blank_postfilter { my $rh_ans=shift; + $rh_ans->{_filter_name} = 'blank_postfilter'; return($rh_ans) unless defined($rh_ans->{error_flag}) and $rh_ans->{error_flag} eq 'BLANK'; $rh_ans->{error_flag} = undef; $rh_ans->{error_message} = ''; |
From: dpvc v. a. <we...@ma...> - 2005-07-04 19:42:41
|
Log Message: ----------- Fixed problem with sorting (problem with $a, $b, I think). Modified Files: -------------- pg/lib/Parser/Context: Strings.pm Revision Data ------------- Index: Strings.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser/Context/Strings.pm,v retrieving revision 1.5 retrieving revision 1.6 diff -Llib/Parser/Context/Strings.pm -Llib/Parser/Context/Strings.pm -u -r1.5 -r1.6 --- lib/Parser/Context/Strings.pm +++ lib/Parser/Context/Strings.pm @@ -28,7 +28,7 @@ my $self = shift; my $data = $self->{context}->{$self->{dataName}}; my $single = ''; my @multi = (); - foreach my $x (sort Value::Context::Data::byName (keys %{$data})) { + foreach my $x (sort byName (keys %{$data})) { unless ($data->{$x}{hidden}) { if ($data->{$x}{caseSensitive} || uc($x) eq lc($x)) { if (length($x) == 1) {$single .= $x} @@ -44,6 +44,16 @@ } # +# Must be in the same package as the sort call +# (due to global $a and $b, I assume) +# +sub byName { + my $result = length($b) <=> length($a); + return $result unless $result == 0; + return $a cmp $b; +} + +# # Same as Value::Context::Data::getPattern, but with # the protectRegexp already done on the @multi list. # |
From: dpvc v. a. <we...@ma...> - 2005-07-04 16:24:07
|
Log Message: ----------- Handle TeX versions of variables whose names include underscores better. Modified Files: -------------- pg/lib/Parser: Variable.pm Revision Data ------------- Index: Variable.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser/Variable.pm,v retrieving revision 1.6 retrieving revision 1.7 diff -Llib/Parser/Variable.pm -Llib/Parser/Variable.pm -u -r1.6 -r1.7 --- lib/Parser/Variable.pm +++ lib/Parser/Variable.pm @@ -105,7 +105,7 @@ my $self = shift; my $name = $self->{name}; my $value = $self->{equation}{values}{$name}; return $value if defined($value); - $name = $1.'_{'.$2.'}' if ($name =~ m/^(\D+)(\d+)$/); + $name = $1.'_{'.$2.'}' if ($name =~ m/^([^_]+)_?(\d+)$/); return $name; } # |
From: dpvc v. a. <we...@ma...> - 2005-07-04 15:56:32
|
Log Message: ----------- Better messages when adaptive parameters are too big. Modified Files: -------------- pg/lib/Value: Formula.pm Revision Data ------------- Index: Formula.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Formula.pm,v retrieving revision 1.28 retrieving revision 1.29 diff -Llib/Value/Formula.pm -Llib/Value/Formula.pm -u -r1.28 -r1.29 --- lib/Value/Formula.pm +++ lib/Value/Formula.pm @@ -420,12 +420,13 @@ # # Get parameter values and recompute the points using them # - my @a; my $i = 0; my $max = $l->getFlag('max_adapt',1E8); + my @a; my $i = 0; my $max = Value::Real->new($l->getFlag('max_adapt',1E8)); foreach my $row (@{$B->[0]}) { if (abs($row->[0]) > $max) { - $l->Error("Constant of integration is too large: $row->[0]") - if ($params[$i] eq 'C0'); - $l->Error("Adaptive constant is too large: $params[$i] = $row->[0]"); + $l->Error("Constant of integration is too large: ".$row->[0]->string."\n". + "(maximum allowed is ".$max->string.")") if ($params[$i] eq 'C0'); + $l->Error("Adaptive constant is too large: $params[$i] = ".$row->[0]->string."\n". + "(maximum allowed is ".$max->string.")"); } push @a, $row->[0]; $i++; } |
From: dpvc v. a. <we...@ma...> - 2005-07-04 15:47:10
|
Log Message: ----------- Implement "remove trailing zeros" format, like in prfmt(). I still think '%g' works better in most cases ('%f#' will print larger values without using scientific notation, which is nice, but will print small numbers like .000000005 as 0, even though the fuzzy comparison of this to zero will not be true.) Modified Files: -------------- pg/lib/Value: Real.pm Revision Data ------------- Index: Real.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Real.pm,v retrieving revision 1.15 retrieving revision 1.16 diff -Llib/Value/Real.pm -Llib/Value/Real.pm -u -r1.15 -r1.16 --- lib/Value/Real.pm +++ lib/Value/Real.pm @@ -202,7 +202,10 @@ my $self = shift; my $equation = shift; my $prec = shift; my $n = $self->{data}[0]; my $format = ($equation->{context} || $$Value::context)->{format}{number}; - $n = sprintf($format,$n) if $format; # use the specified precision, if any + if ($format) { + $n = sprintf($format,$n); + if ($format =~ m/#\s*$/) {$n =~ s/(\.\d*?)0*#$/$1/; $n =~ s/\.$//} + } $n = uc($n); # force e notation to E $n = 0 if $self == 0; # make near zero print as zero $n = "(".$n.")" if ($n < 0 || $n =~ m/E/i) && defined($prec) && $prec >= 1; |
From: Mike G. v. a. <we...@ma...> - 2005-07-04 15:26:13
|
Log Message: ----------- Commenting out unused code. This file still needs a lot of work. Modified Files: -------------- pg/macros: PGdiffeqmacros.pl Revision Data ------------- Index: PGdiffeqmacros.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/PGdiffeqmacros.pl,v retrieving revision 1.2 retrieving revision 1.3 diff -Lmacros/PGdiffeqmacros.pl -Lmacros/PGdiffeqmacros.pl -u -r1.2 -r1.3 --- macros/PGdiffeqmacros.pl +++ macros/PGdiffeqmacros.pl @@ -351,198 +351,7 @@ #The inputs are given as arguments viz: ivy ($a,$b,$c,$m,$n). #The output is the solution as a string giving a function of t. -#sub oldivy { -# # the two roots are: -b/(2a) plus/minus sqrt(b*b - 4*a*c)/(2a) -# my ($a,$b,$c,$m,$n) = @_; -# my $denom = 2*$a ; -# my $discriminant = ($b*$b)-(4*$a*$c); -# my $outa = frac(-$b,2*$a); -# my $dabs = abs($discriminant); -# my $ira = rad(1,2*$a,$dabs); -# -# # let $r = "-$b/(2*$a)" where the fraction is in lowest form. -# my $r = $outa->{displaystr}; -# -# # make sure you take a square root of positive number. -# # let $w = sqrt(abs(-b/(2a))) -# my $w = sqrt($dabs); -# -# # let $irra = "sqrt($dabs)/(2a)" in lowest form. -# my $irra = $ira->{displaystr}; -# -# my @suma = add($r,$b,$irra,$discriminant); -# my @sumb = add($r,$b,"-$irra",$discriminant); -# -# # $r1 is string of root1. $r2 is string of root2. -# -# # let $r2 = "$r + $irra" = "(-$b+sqrt($dabs))/(2a)". -# my $r2 = $suma[0]; -# -# # let $r1 = "$r - $irra" = "(-$b-sqrt($dabs))/(2a)". -# my $r1 = $sumb[0]; -# -# # if the sqrt(discriminant) is an integer (real solutions)... -# if ($w == int($w)) { -# my $outb = frac(-$b-$w,$denom); -# my $outc = frac(-$b+$w,$denom); -# -# # then let $r1 = (-$b-sqrt($dabs))/(2a). the "plus" term. -# $r1 = $outb->{displaystr}; -# -# # then let $r2 = (-$b+sqrt($dabs))/(2a). the "minus" term. -# $r2 = $outc->{displaystr}; -# } -# -####### -# # let $exp = "exp( (-$b)/(2a) t)"; -# my $exp = simpleexp ($r ,0 ); -# -# #first find a fundamental set of solutions. -# #initialize: -# -# # Solution: Ae^(($r1)t) + Be^(($r2)t) = y(t) -# # $y1 = e^(($r1)t), $y2 = e^(($r2)t) -# # $y1prime = ($r1)*e^(($r1)t), $y2prime = ($r2)*e^(($r2)t) -# -# my $y1 = "0"; -# my $y2 = "0"; -# my $y1prime = "0"; -# my $y2prime = "0"; -# -# if ($discriminant==0) { -# $y1 = simpleexp ($r,0); -# $y2 = "t* $y1 "; -# $y1prime = "$r *$y1"; -# $y2prime = "(1 + $r*t)*$y1 "; -# if ($r eq "0") { -# $y1prime = "0"; -# $y2prime = "1"; -# $y2 = "t"; -# } -# } -# -# # if the discriminant is positive, -# if ($discriminant>0) { -# $y1 = simpleexp ($r1,0); -# $y2 = simpleexp ($r2,0); -# $y1prime = "($r1)*$y1"; -# $y2prime = "($r2)*$y2"; -# } -# -# # if the discriminant is negative, then the roots are imaginary. -# # recall e^(ix) = cos x + isin x -# # and e^(ix) + e^(-ix) = 2cos x -# -# if ( $discriminant<0) { -# my $arg = "($irra )*t"; -# if ($w == $denom) { -# $arg = "t"; -# } -# my $cos = "cos($arg ) "; -# my $sin = "sin($arg ) "; -# -# if ($b == 0) { -# $y1 = "$cos"; -# $y2 = "$sin"; -# $y1prime = "(-$irra)*$y2"; -# $y2prime = "($irra)*$y1"; -# } else { -# $y1 = "($exp)*($cos) "; -# $y2 = "($exp)*($sin) "; -# $y1prime = "(-$irra)*($y2) + ($r)*($y1)"; -# $y2prime = "($irra)*($y1) + ($r)*($y2)"; -# } -# } -# print ("discriminant=$discriminant\n"); -# -######Need to get coef below to solve iv problem -#### Used Cramer's rule -##initialize -# my $coef1 = "0"; -# my $coef2 = "0"; -# my $t1 = 0; -# my $t2 = 0; -# my $out = frac(2,1); -# -# my $k = ($m*$b*.5)+($a*$n); -# my $kk = ($m*$b)+(2*$a*$n); -# -# # if the discriminant is zero, then you have twin roots. ($r1=$r2) -# # so, Ae^(($r1)t)+Bte^(($r2)t) = Ae^(($r1)0)+B*0*e^(($r2)0) = y(0) = A+Bt = A = m. -# -# if ($discriminant==0) { -# $coef1 = "$m"; -# #$coef2 = "$kk /$denom"; -# $out = frac($kk, $denom); -# $coef2 = $out->{displaystr}; -# $t1 = $m; -# $t2 = $kk; -# } elsif ($discriminant>0) { -# my $coef1numb = $w*$m - $kk; -# my $coef2numb = $w*$m + $kk; -# $t1 = $coef1numb; -# $t2 = $coef2numb; -# my $teiler = 2*$w; -# my $test = int($w); -# if ($w == $test) { -# #$coef1 = "$coef1numb / $teiler"; -# $out = frac($t1,$teiler); -# $coef1 = $out->{displaystr}; -# #$coef2 = "$coef2numb / $teiler"; -# $out = frac($t2,$teiler); -# $coef2 = $out->{displaystr}; -# } else { -# my $dd = 2*$discriminant; -# my $irc = rad($kk,$dd,$discriminant); -# my $irrc = $irc->{displaystr}; -# my $mfirst = frac($m,2); -# my $mf = $mfirst->{displaystr} ; -# my @sumc = add($mf,$m,$irrc,$kk); -# my @sumd = add($mf,$m,"-($irrc)",$kk); -# -# # coef1 = coefficient of the first solution, coef2 = coefficient of second solution -# -# # let $coef2 = "$mf+$kk/(2*sqrt($discriminant))"; -# $coef2 = $sumc[0]; -# -# # let $coef1= "$mf-$kk/(2*sqrt($discriminant))"; -# $coef1 = $sumd[0]; -# } -# } else { -# -# # $coef2 = ($kk)/sqrt($dabs) = ($kk/$dabs)*sqrt($dabs) -# $coef1 = "$m"; -# my $ire = rad($kk,$dabs,$dabs); -# $coef2 = $ire->{displaystr}; -# $t1 = $m; -# $t2 = $kk; -# if ($w ==int($w)) { -# my $outd = frac($kk,$w); -# # let $coef2 = "$kk/$w"; -# $coef2 = $outd->{displaystr}; -# } -# } -# -# my $z1 = "($coef1 ) *$y1"; -# if ($y1 eq "1") { -# $z1 = $coef1; -# } -# -# my $z2 = "($coef2 ) *$y2"; -# -# # if you have "($coef2)*1" then just write "$coef2". -# if ($y2 eq "1") { -# $z2 = $coef2; -# } -# -# # $z1 = Ae^(($r1)t), $z2 = Be^(($r2)t) -# # $t1 = numerical value of A, $t2 = numerical value of B -# -# my @sume = add($z1,$t1,$z2,$t2); -# my $y = $sume[0] ; -# $y; -#} -#### + sub undeterminedExp { my ($A,$B,$C,$r,$q0,$q1,$q2) = @_; |
From: dpvc v. a. <we...@ma...> - 2005-07-04 15:25:52
|
Log Message: ----------- Don't use the overloaded Real class for doing adaptive parameter checks (use non-fuzzy checks, since we are checking the fuzziness by hand). Modified Files: -------------- pg/lib/Value: Formula.pm Revision Data ------------- Index: Formula.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Formula.pm,v retrieving revision 1.27 retrieving revision 1.28 diff -Llib/Value/Formula.pm -Llib/Value/Formula.pm -u -r1.27 -r1.28 --- lib/Value/Formula.pm +++ lib/Value/Formula.pm @@ -188,9 +188,9 @@ my $zeroLevel = $self->getFlag('zeroLevel',1E-14); foreach $i (0..scalar(@{$lvalues})-1) { my $tol = $tolerance; - $tol *= abs($lvalues->[$i]) if $isRelative && abs($lvalues->[$i]) > $zeroLevel; - return $rvalues->[$i]->value <=> $avalues->[$i]->value - unless abs($rvalues->[$i] - $avalues->[$i]) < $tol; + my ($lv,$rv,$av) = ($lvalues->[$i]->value,$rvalues->[$i]->value,$avalues->[$i]->value); + $tol *= abs($lv) if $isRelative && abs($lv) > $zeroLevel; + return $rv <=> $av unless abs($rv - $av) < $tol; } return 0; } |