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: jj v. a. <we...@ma...> - 2005-08-26 17:28:56
|
Log Message: ----------- When dealing with showOldAnswers defaults, allow control of who get get the system default after the due date instead of hard-wiring it. Default value is that students don't initially get old answers after the due date, but professors do. Modified Files: -------------- webwork-modperl/conf: global.conf.dist webwork-modperl/lib/WeBWorK/ContentGenerator: Problem.pm Revision Data ------------- Index: global.conf.dist =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/conf/global.conf.dist,v retrieving revision 1.131 retrieving revision 1.132 diff -Lconf/global.conf.dist -Lconf/global.conf.dist -u -r1.131 -r1.132 --- conf/global.conf.dist +++ conf/global.conf.dist @@ -514,7 +514,11 @@ show_correct_answers_before_answer_date => $ta, show_solutions_before_answer_date => $ta, avoid_recording_answers => $ta, + # Below this level, old answers are never initially shown can_show_old_answers_by_default => $student, + # at this level, we look at showOldAnswers for default value + # even after the due date + can_always_use_show_old_answers_default => $professor, check_answers_before_open_date => $ta, check_answers_after_open_date_with_attempts => $ta, check_answers_after_open_date_without_attempts => $guest, Index: Problem.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Problem.pm,v retrieving revision 1.183 retrieving revision 1.184 diff -Llib/WeBWorK/ContentGenerator/Problem.pm -Llib/WeBWorK/ContentGenerator/Problem.pm -u -r1.183 -r1.184 --- lib/WeBWorK/ContentGenerator/Problem.pm +++ lib/WeBWorK/ContentGenerator/Problem.pm @@ -177,6 +177,9 @@ # Reset the default in some cases sub set_showOldAnswers_default { my ($self, $ce, $userName, $authz, $set) = @_; + # these people always use the system/course default, so don't + # override the value of ...->{showOldAnswers} + return if $authz->hasPermissions($userName, "can_always_use_show_old_answers_default"); # this person should always default to 0 $ce->{pg}->{options}->{showOldAnswers} = 0 unless ($authz->hasPermissions($userName, "can_show_old_answers_by_default")); |
From: Sam H. v. a. <we...@ma...> - 2005-08-26 16:51:53
|
Log Message: ----------- improved handling of symlinks in listFilesRecursiveHelper. symlinks to files are now treated as files instead of directories. Modified Files: -------------- webwork2/lib/WeBWorK: Utils.pm Revision Data ------------- Index: Utils.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/Utils.pm,v retrieving revision 1.66 retrieving revision 1.67 diff -Llib/WeBWorK/Utils.pm -Llib/WeBWorK/Utils.pm -u -r1.66 -r1.67 --- lib/WeBWorK/Utils.pm +++ lib/WeBWorK/Utils.pm @@ -176,7 +176,24 @@ foreach my $dir_entry (@dir_contents) { my $full_path = "$full_dir/$dir_entry"; - if (-d $full_path or -l $full_path) { + + # determine whether the entry is a directory or a file, taking into account the + my $is_dir; + my $is_file; + if (-l $full_path) { + my $link_target = "$full_dir/" . readlink $full_path; + if ($link_target) { + $is_dir = -d $link_target; + $is_file = !$is_dir && -f $link_target || -p $link_target || -S $link_target; + } else { + warn "Couldn't resolve symlink $full_path: $!"; + } + } else { + $is_dir = -d $full_path; + $is_file = !$is_dir && -f $full_path || -p $full_path || -S $full_path; + } + + if ($is_dir) { # standard things to skip next if $dir_entry eq "."; next if $dir_entry eq ".."; @@ -196,12 +213,15 @@ # everything looks good, time to recurse! push @matches, listFilesRecursiveHelper($base_dir, $subdir, $match_qr, $prune_qr, $match_full, $prune_full); - } elsif (-f $full_path or -p $full_path or -S $full_path) { + } elsif ($is_file) { my $file = ($curr_dir eq "") ? $dir_entry : "$curr_dir/$dir_entry"; my $match_string = $match_full ? $file : $dir_entry; if (not defined $match_string or $match_string =~ m/$match_qr/) { push @matches, $file; } + } else { + # otherwise, it's a character device or a block device, and i don't + # suppose we want anything to do with those ;-) } } |
From: jj v. a. <we...@ma...> - 2005-08-26 14:05:29
|
Log Message: ----------- Fix bug where perl functions which may need parentheses by forcing them when the function starts Parser::Function->. This seems to work, but there might be a better way to do it, or need other cleaning up. Modified Files: -------------- pg/lib/Parser: Function.pm Revision Data ------------- Index: Function.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser/Function.pm,v retrieving revision 1.15 retrieving revision 1.16 diff -Llib/Parser/Function.pm -Llib/Parser/Function.pm -u -r1.15 -r1.16 --- lib/Parser/Function.pm +++ lib/Parser/Function.pm @@ -284,7 +284,7 @@ my $fn = $self->{def}; my @p = (); my $perl; foreach my $x (@{$self->{params}}) {push(@p,$x->perl)} if ($fn->{perl}) {$perl = $fn->{perl}.'('.join(',',@p).')'} - else {$perl = 'Parser::Function->call('.join(',',"'$self->{name}'",@p).')'} + else {return('(Parser::Function->call('.join(',',"'$self->{name}'",@p).'))')} $perl = '('.$perl.')' if $parens == 1; return $perl; } |
From: jj v. a. <we...@ma...> - 2005-08-26 04:18:01
|
Log Message: ----------- When making on-the-fly image names, replace periods in the student login name to avoid problems with pdflatex. Changed the replacement in set names to use the same new trick (the old way had an infintesimal chance of collision). The new scheme is first double all instances of "Q". Then replace periods with "-Q-". Modified Files: -------------- pg/macros: PGgraphmacros.pl Revision Data ------------- Index: PGgraphmacros.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/PGgraphmacros.pl,v retrieving revision 1.5 retrieving revision 1.6 diff -Lmacros/PGgraphmacros.pl -Lmacros/PGgraphmacros.pl -u -r1.5 -r1.6 --- macros/PGgraphmacros.pl +++ macros/PGgraphmacros.pl @@ -107,8 +107,12 @@ # select a name for this graph based on the user, the psvn and the problem my $setName = $main::setNumber; # replace dots in set name to keep latex happy - $setName =~ s/\./d0T/g; - my $imageName = "$main::studentLogin-$main::problemSeed-set${setName}prob${main::probNum}"; + $setName =~ s/Q/QQ/g; + $setName =~ s/\./-Q-/g; + my $studentLogin = $main::studentLogin; + $studentLogin =~ s/Q/QQ/g; + $studentLogin =~ s/\./-Q-/g; + my $imageName = "$studentLogin-$main::problemSeed-set${setName}prob${main::probNum}"; # $imageNum counts the number of graphs with this name which have been created since PGgraphmacros.pl was initiated. my $imageNum = ++$main::images_created{$imageName}; # this provides a unique name for the graph -- it does not include an extension. |
From: Gavin L. v. a. <we...@ma...> - 2005-08-25 20:00:28
|
Log Message: ----------- Add some electrical units. Take two, hopefully in the correct branch this time. Modified Files: -------------- pg/lib: Units.pm Revision Data ------------- Index: Units.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Units.pm,v retrieving revision 1.2 retrieving revision 1.3 diff -Llib/Units.pm -Llib/Units.pm -u -r1.2 -r1.3 --- lib/Units.pm +++ lib/Units.pm @@ -41,6 +41,7 @@ 'degF' => 0, 'degK' => 0, 'mol' => 0, # moles, treated as a fundamental unit? + 'amp' => 0, ); # This hash contains all of the units which will be accepted. These must @@ -80,9 +81,13 @@ 'degK' => 1 }, 'mol' => { - 'factor' =>1, - 'mol' =>1 - }, + 'factor' =>1, + 'mol' =>1 + }, + 'amp' => { + 'factor' => 1, + 'amp' => 1, + }, # ANGLES # deg -- degrees # @@ -397,7 +402,61 @@ 'kg' => 1, 's' => -2 }, - +# ELECTRICAL UNITS (incomplete) +# C -- Coulomb +# V -- volt +# mV -- milivolt +# kV -- kilovolt +# MV -- megavolt +# F -- Farad +# mF -- miliFarad +# uF -- microFarad +# ohm -- ohm +# kohm -- kilo-ohm + 'C' => { + 'factor' => 1, + 'amp' => 1, + 's' => 1, + }, + 'V' => { + 'factor' => 1, + 'J' => 1, + 'C' => -1, + }, + 'mV' => { + 'factor' => 0.001, + 'V' => 1, + }, + 'kV' => { + 'factor' => 1000, + 'V' => 1, + }, + 'MV' => { + 'factor' => 10**(6), + 'V' => 1, + }, + 'F' => { + 'factor' => 1, + 'C' => 1, + 'V' => -1, + }, + 'mF' => { + 'factor' => 0.001, + 'F' => 1, + }, + 'uF' => { + 'factor' => 10**(-6), + 'F' => 1, + }, + 'ohm' => { + 'factor' => 1, + 'V' => 1, + 'amp' => -1, + }, + 'kohm' => { + 'factor' => 1000, + 'ohm' => 1, + }, ); |
From: Gavin L. v. a. <we...@ma...> - 2005-08-25 19:29:46
|
Log Message: ----------- Add basic electrical units. Tags: ---- rel-2-1-a1 Modified Files: -------------- pg/lib: Units.pm Revision Data ------------- Index: Units.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Units.pm,v retrieving revision 1.2 retrieving revision 1.2.8.1 diff -Llib/Units.pm -Llib/Units.pm -u -r1.2 -r1.2.8.1 --- lib/Units.pm +++ lib/Units.pm @@ -40,6 +40,7 @@ 'degC' => 0, 'degF' => 0, 'degK' => 0, + 'amp' => 0, 'mol' => 0, # moles, treated as a fundamental unit? ); @@ -397,7 +398,61 @@ 'kg' => 1, 's' => -2 }, - +# ELECTRICAL UNITS (incomplete) +# C -- Coulomb +# V -- volt +# mV -- milivolt +# kV -- kilovolt +# MV -- megavolt +# F -- Farad +# mF -- miliFarad +# uF -- microFarad +# ohm -- ohm +# kohm -- kilo-ohm + 'C' => { + 'factor' => 1, + 'amp' => 1, + 's' => 1, + }, + 'V' => { + 'factor' => 1, + 'J' => 1, + 's' => -1, + }, + 'mV' => { + 'factor' => 0.001, + 'V' => 1, + }, + 'kV' => { + 'factor' => 1000, + 'V' => 1, + }, + 'MV' => { + 'factor' => 10**(6), + 'V' => 1, + }, + 'F' => { + 'factor' => 1, + 'C' => 1, + 'V' => -1, + }, + 'mF' => { + 'factor' => 0.001, + 'F' => 1, + }, + 'uF' => { + 'factor' => 10**(-6), + 'F' => 1, + }, + 'ohm' => { + 'factor' => 1, + 'V' => 1, + 'amp' => -1, + }, + 'kohm' => { + 'factor' => 1000, + 'ohm' => 1, + }, ); |
From: jj v. a. <we...@ma...> - 2005-08-25 18:30:47
|
Log Message: ----------- Updated instructions to match changes to global.conf.dist and the move or PGanswermacros.pl. Modified Files: -------------- pg/lib/Parser/Legacy: README Revision Data ------------- Index: README =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser/Legacy/README,v retrieving revision 1.2 retrieving revision 1.3 diff -Llib/Parser/Legacy/README -Llib/Parser/Legacy/README -u -r1.2 -r1.3 --- lib/Parser/Legacy/README +++ lib/Parser/Legacy/README @@ -8,30 +8,25 @@ those). To install these changes, update the Parser from the CVS directory -using "cvs update -d" in order to get the new Legacy direcrtory. Then -replace the PGanswermacros.pl file that is in pg/macros with the new -one that is in pg/lib/Parser/Legacy (perhaps renaming the old one as -PGanswermacros.pl-orig in case you want to go back to it). Then edit -the global.conf file and add [qw(Parser::Legacy)] to the $pg{modules} -list (this forces the loading of a few items needed by the new -PGanswermacros but that aren't loaded by default). Finally, restart -the server. Youre WW server should now be using Parser-based answer -checkers for all string and function comparisons. - -I believe that I have covered all the bases with these answer -checkers, but this is a pretty major switch, so there undoubtedly will -be problems. I have arrange to have the original answer macros remain -available, and you can go back to them on either a site-wide, -course-by-course, or problem-by-problem basis. - -If something major goes wrong and you want to go back to using the -original answer checkers, set +using "cvs update -d" in order to get the new Legacy direcrtory. +Then edit the global.conf file, changing the line for +useOldAnswerMacros to: + + $pg{specialPGEnvironmentVars}{useOldAnswerMacros} = 0; + +Your WW server should now be using Parser-based answer checkers for +all string and function comparisons. + +The original answer macros remain available, and you can go back to +them on either a site-wide, course-by-course, or problem-by-problem +basis. To change the setting for the system as a whole change the +line original answer checkers, set $pg{specialPGEnvironmentVars}{useOldAnswerMacros} = 1; in global.conf. This will disable the new answer checkers on a site-wide basis. You can set this variable in a course.conf file to -change back for just one course. +set things one way or the other for just one course. If a particular problem file is causing trouble, you can set it to use the original answer checkers by putting @@ -41,9 +36,9 @@ at the top of the .pg file. This will cause the std_num_cmp() and other answer checkers to revert to the original versions. -If you have set the useOldAnswerMacros value in the global.conf or -course.conf file, you can still use the new Parser-based checkers in -individual problem files by putting +If you have set the useOldAnswerMacros value to 0 in the global.conf +or course.conf file, you can still use the new Parser-based checkers +in individual problem files by putting $useOldAnswerMacros = 0; |
From: jj v. a. <we...@ma...> - 2005-08-25 18:23:43
|
Log Message: ----------- A separate copy of this file is no longer needed. Removed Files: ------------- pg/lib/Parser/Legacy: PGanswermacros.pl Revision Data ------------- --- lib/Parser/Legacy/PGanswermacros.pl +++ /dev/null @@ -1,4851 +0,0 @@ -# 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.11 2005/08/24 11:44:49 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!)) { - $user_context = main::PG_restricted_eval(q!\%context!); - $Context = sub {Parser::Context->current($user_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}); - - # - # Hack to fix up exponential notation in correct answer - # (e.g., perl will pass .0000001 as 1e-07). - # - $correctAnswer = uc($correctAnswer) - if $correctAnswer =~ m/e/ && Value::isNumber($correctAnswer); - - # - # Get an apppropriate context based on the mode - # - my $context; - for ($mode) { - /^strict$/i and do { - $context = $Parser::Context::Default::context{LimitedNumeric}->copy; - last; - }; - /^arith$/i and do { - $context = $Parser::Context::Default::context{LegacyNumeric}->copy; - $context->functions->disable('All'); - last; - }; - /^frac$/i and do { - $context = $Parser::Context::Default::context{'LimitedNumeric-Fraction'}->copy; - last; - }; - - # default - $context = $Parser::Context::Default::context{LegacyNumeric}->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 ($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 'absolute') { - $context->flags->set( - tolerance => $num_params{tolerance}, - tolType => 'absolute', - ); - } else { - $context->flags->set( - tolerance => .01*$num_params{tolerance}, - tolType => 'relative', - ); - } - $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 ($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->install_post_filter(sub { - my $rh_ans = shift; - $rh_ans->{student_ans} = $rh_ans->{student_value}->string - if ref($rh_ans->{student_value}); - 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 integration in the answer x^3/3 + c. - debug -- when set to 1 this provides extra information while checking the - the answer. - - Returns an answer evaluator, or (if given a reference to an array - of answers), a list of answer evaluators - -ANSWER: - - The answer must be in the form of a string. The answer can contain - functions, pi, e, and arithmetic operations. However, the correct answer - string follows a slightly stricter syntax than student answers; specifically, - there is no implicit multiplication. So the correct answer must be "3*x" rather - than "3 x". Students can still enter "3 x". - -VARIABLES: - - The var parameter can contain either a number or a reference to an array of - variable names. If it contains a number, the variables are named automatically - as follows: 1 variable -- x - 2 variables -- x, y - 3 variables -- x, y, z - 4 or more -- x_1, x_2, x_3, etc. - If the var parameter contains a reference to an array of variable names, then - the number of variables is determined by the number of items in the array. A - reference to an array is created with brackets, e.g. "var => ['r', 's', 't']". - If only one variable is being used, you can write either "var => ['t']" for - consistency or "var => 't'" as a shortcut. The default is one variable, x. - -LIMITS: - - Limits are specified with the limits parameter. You may NOT use llimit/ulimit. - If you specify limits for one variable, you must specify them for all variables. - The limit parameter must be a reference to an array of arrays of the form - [lower_limit. upper_limit], each array corresponding to the lower and upper - endpoints of the (half-open) domain of one variable. For example, - "vars => 2, limits => [[0,2], [-3,8]]" would cause x to be evaluated in [0,2) and - y to be evaluated in [-3,8). If only one variable is being used, you can write - either "limits => [[0,3]]" for consistency or "limits => [0,3]" as a shortcut. - -TEST POINTS: - - In some cases, the problem writer may want to specify the points - used to check a particular function. For example, if you want to - use only integer values, they can be specified. With one variable, - you can specify "test_points => [1,4,5,6]" or "test_points => [[1,4,5,6]]". - With more variables, specify the list for the first variable, then the - second, and so on: "vars=>['x','y'], test_points => [[1,4,5],[7,14,29]]". - - If the problem writer wants random values which need to meet some special - restrictions (such as being integers), they can be generated in the problem: - "test_points=>[random(1,50), random(1,50), random(1,50), random(1,50)]". - - Note that test_points should not be used for function checks which involve - parameters (either explicitly given by "params", or as antiderivatives). - -EXAMPLES: - - fun_cmp( "3*x" ) -- standard compare, variable is x - fun_cmp( ["3*x", "4*x+3", "3*x**2"] )... [truncated message content] |
From: jj v. a. <we...@ma...> - 2005-08-25 18:20:15
|
Log Message: ----------- This makes the pg/lib/Parser/Legacy copy of PGanswermacros.pl the official copy here. This still leaves the original answer evaluators as the default, but simplifies the update process, and we don't have to maintain two copies of the original functions. Modified Files: -------------- pg/macros: PGanswermacros.pl Revision Data ------------- Index: PGanswermacros.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/PGanswermacros.pl,v retrieving revision 1.34 retrieving revision 1.35 diff -Lmacros/PGanswermacros.pl -Lmacros/PGanswermacros.pl -u -r1.34 -r1.35 --- macros/PGanswermacros.pl +++ macros/PGanswermacros.pl @@ -1,5 +1,3 @@ - - # This file is PGanswermacros.pl # This includes the subroutines for the ANS macros, that # is, macros allowing a more flexible answer checking @@ -135,7 +133,8 @@ $useBaseTenLog , $inputs_ref , $QUESTIONNAIRE_ANSWERS , - + $user_context, + $Context, ); @@ -166,6 +165,11 @@ $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!)) { + $user_context = main::PG_restricted_eval(q!\%context!); + $Context = sub {Parser::Context->current($user_context,@_)}; + } } @@ -1017,7 +1021,133 @@ zeroLevel=>$zeroLevel, zeroLevelTol=>$zeroLevelTol); } -sub NUM_CMP { # low level numeric compare +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}); + + # + # Hack to fix up exponential notation in correct answer + # (e.g., perl will pass .0000001 as 1e-07). + # + $correctAnswer = uc($correctAnswer) + if $correctAnswer =~ m/e/ && Value::isNumber($correctAnswer); + + # + # Get an apppropriate context based on the mode + # + my $context; + for ($mode) { + /^strict$/i and do { + $context = $Parser::Context::Default::context{LimitedNumeric}->copy; + last; + }; + /^arith$/i and do { + $context = $Parser::Context::Default::context{LegacyNumeric}->copy; + $context->functions->disable('All'); + last; + }; + /^frac$/i and do { + $context = $Parser::Context::Default::context{'LimitedNumeric-Fraction'}->copy; + last; + }; + + # default + $context = $Parser::Context::Default::context{LegacyNumeric}->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 ($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 'absolute') { + $context->flags->set( + tolerance => $num_params{tolerance}, + tolType => 'absolute', + ); + } else { + $context->flags->set( + tolerance => .01*$num_params{tolerance}, + tolType => 'relative', + ); + } + $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 ($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->install_post_filter(sub { + my $rh_ans = shift; + $rh_ans->{student_ans} = $rh_ans->{student_value}->string + if ref($rh_ans->{student_value}); + 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 ); @@ -1375,7 +1505,7 @@ # allow var => 'x' as an abbreviation for var => ['x'] my %out_options = %opt; - unless ( ref($out_options{var}) eq 'ARRAY' ) { + unless ( ref($out_options{var}) eq 'ARRAY' || $out_options{var} =~ m/^\d+$/) { $out_options{var} = [$out_options{var}]; } # allow params => 'c' as an abbreviation for params => ['c'] @@ -1730,6 +1860,188 @@ sub FUNCTION_CMP { + return ORIGINAL_FUNCTION_CMP(@_) + if main::PG_restricted_eval(q!$main::useOldAnswerMacros!); + + my %func_params = @_; + + my $correctEqn = $func_params{'correctEqn'}; + my $var = $func_params{'var'}; + my $ra_limits = $func_params{'limits'}; + my $tol = $func_params{'tolerance'}; + my $tolType = $func_params{'tolType'}; + my $numPoints = $func_params{'numPoints'}; + my $mode = $func_params{'mode'}; + my $maxConstantOfIntegration = $func_params{'maxConstantOfIntegration'}; + my $zeroLevel = $func_params{'zeroLevel'}; + my $zeroLevelTol = $func_params{'zeroLevelTol'}; + my $testPoints = $func_params{'test_points'}; + + # + # Check that everything is defined: + # + $func_params{debug} = 0 unless defined $func_params{debug}; + $mode = 'std' unless defined $mode; + my @VARS = get_var_array($var); + my @limits = get_limits_array($ra_limits); + my @PARAMS = @{$func_params{'params'} || []}; + + if($tolType eq 'relative') { + $tol = $functRelPercentTolDefault unless defined $tol; + $tol *= .01; + } else { + $tol = $functAbsTolDefault unless defined $tol; + } + + # + # Ensure that the number of limits matches number of variables + # + foreach my $i (0..scalar(@VARS)-1) { + $limits[$i][0] = $functLLimitDefault unless defined $limits[$i][0]; + $limits[$i][1] = $functULimitDefault unless defined $limits[$i][1]; + } + + # + # Check that the test points are array references with the right number of coordinates + # + if ($testPoints) { + my $n = scalar(@VARS); my $s = ($n != 1)? "s": ""; + foreach my $p (@{$testPoints}) { + $p = [$p] unless ref($p) eq 'ARRAY'; + warn "Test point (".join(',',@{$p}).") should have $n coordiante$s" + unless scalar(@{$p}) == $n; + } + } + + $numPoints = $functNumOfPoints unless defined $numPoints; + $maxConstantOfIntegration = $functMaxConstantOfIntegration unless defined $maxConstantOfIntegration; + $zeroLevel = $functZeroLevelDefault unless defined $zeroLevel; + $zeroLevelTol = $functZeroLevelTolDefault unless defined $zeroLevelTol; + + $func_params{'var'} = \@VARS; + $func_params{'params'} = \@PARAMS; + $func_params{'limits'} = \@limits; + $func_params{'tolerance'} = $tol; + $func_params{'tolType'} = $tolType; + $func_params{'numPoints'} = $numPoints; + $func_params{'mode'} = $mode; + $func_params{'maxConstantOfIntegration'} = $maxConstantOfIntegration; + $func_params{'zeroLevel'} = $zeroLevel; + $func_params{'zeroLevelTol'} = $zeroLevelTol; + + ######################################################## + # End of cleanup of calling parameters + ######################################################## + + my %options = (debug => $func_params{'debug'}); + + # + # Initialize the context for the formula + # + my $context = $Parser::Context::Default::context{"LegacyNumeric"}->copy; + $context->flags->set( + tolerance => $func_params{'tolerance'}, + tolType => $func_params{'tolType'}, + zeroLevel => $func_params{'zeroLevel'}, + zeroLevelTol => $func_params{'zeroLevelTol'}, + num_points => $func_params{'numPoints'}, + ); + if ($func_params{'mode'} eq 'antider') { + $context->flags->set(max_adapt => $func_params{'maxConstantOfIntegration'}); + $options{upToConstant} = 1; + } + + # + # Add the variables and parameters to the context + # + my %variables; my $x; + foreach $x (@{$func_params{'var'}}) { + if (length($x) > 1) { + $context->{_variables}->{pattern} = $context->{_variables}->{namePattern} = + $x . '|' . $context->{_variables}->{pattern}; + $context->update; + } + $variables{$x} = 'Real'; + } + foreach $x (@{$func_params{'params'}}) {$variables{$x} = 'Parameter'} + $context->variables->are(%variables); + + # + # Create the Formula object and get its answer checker + # + my $oldContext = &$Context($context); + my $f = new Value::Formula($correctEqn); + $f->{limits} = $func_params{'limits'}; + $f->{test_points} = $func_params{'test_points'}; + my $cmp = $f->cmp(%options); + $cmp->{debug} = 1 if $func_params{'debug'}; + &$Context($oldContext); + + # + # Get previous answer from hidden field of form + # + $cmp->install_pre_filter( + sub { + my $rh_ans = shift; + $rh_ans->{_filter_name} = "fetch_previous_answer"; + my $prev_ans_label = "previous_".$rh_ans->{ans_label}; + $rh_ans->{prev_ans} = + (defined $inputs_ref->{$prev_ans_label} and + $inputs_ref->{$prev_ans_label} =~/\S/) ? $inputs_ref->{$prev_ans_label} : undef; + $rh_ans; + } + ); + + # + # Parse the previous answer, if any + # + $cmp->install_pre_filter( + sub { + my $rh_ans = shift; + $rh_ans->{_filter_name} = "parse_previous_answer"; + return $rh_ans unless defined $rh_ans->{prev_ans}; + $rh_ans->{prev_formula} = Parser::Formula($rh_ans->{prev_ans}); + $rh_ans; + } + ); + + # + # Check if previous answer equals this current one + # + $cmp->install_evaluator( + sub { + my $rh_ans = shift; + $rh_ans->{_filter_name} = "compare_to_previous_answer"; + return $rh_ans unless defined($rh_ans->{prev_formula}) && defined($rh_ans->{student_formula}); + $rh_ans->{prev_equals_current} = + Value::cmp_compare($rh_ans->{student_formula},$rh_ans->{prev_formula},{}); + $rh_ans; + } + ); + + # + # Produce a message if the previous answer equals this one + # (and is not correct, and is not specified the same way) + # + $cmp->install_post_filter( + sub { + my $rh_ans = shift; + $rh_ans->{_filter_name} = "produce_equivalence_message"; + return $rh_ans unless $rh_ans->{prev_equals_current} && $rh_ans->{score} == 0; + return $rh_ans if $rh_ans->{prev_ans} eq $rh_ans->{original_student_ans}; + $rh_ans->{ans_message} = "This answer is equivalent to the one you just submitted or previewed."; + $rh_ans; + } + ); + + return $cmp; +} + +# +# The original version, for backward compatibility +# (can be removed when the Parser-based version is more fully tested.) +# +sub ORIGINAL_FUNCTION_CMP { my %func_params = @_; my $correctEqn = $func_params{'correctEqn'}; @@ -1961,6 +2273,13 @@ sub { my $rh_ans = shift; return $rh_ans unless defined $rh_ans->{ra_diff_with_prev_ans}; + ## + ## DPVC -- only give the message if the answer is specified differently + ## + return $rh_ans if $rh_ans->{prev_ans} eq $rh_ans->{student_ans}; + ## + ## /DPVC + ## is_zero_array($rh_ans, stdin => 'ra_diff_with_prev_ans', stdout => 'ans_equals_prev_ans' @@ -2000,7 +2319,8 @@ sub { my $rh_ans = shift; if ( defined($rh_ans->{'ans_equals_prev_ans'}) and $rh_ans->{'ans_equals_prev_ans'} and $rh_ans->{score}==0) { - $rh_ans->{ans_message} = "This answer is the same as the one you just submitted or previewed."; +## $rh_ans->{ans_message} = "This answer is the same as the one you just submitted or previewed."; + $rh_ans->{ans_message} = "This answer is equivalent to the one you just submitted or previewed."; ## DPVC } $rh_ans; } @@ -2984,11 +3304,7 @@ } else { #default to the x_1, x_2, ... convention my ($i, $tag); - for( $i=0; $i < $in; $i++ ) { - ## akp the above seems to be off by one 1/4/00 - $tag = $i + 1; ## akp 1/4/00 - $out[$i] = "${functVarDefault}_" . $tag; ## akp 1/4/00 - } + for($i = 0; $i < $in; $i++) {$out[$i] = "${functVarDefault}_".($i+1)} } return @out; } @@ -3671,7 +3987,6 @@ } # no output if error_msg_flag is set to 0. - $rh_ans; } |
From: jj v. a. <we...@ma...> - 2005-08-25 18:16:05
|
Log Message: ----------- Load Parser::Legacy. This change is needed for the current version of extraAnswerEvaluators.pl to work out of the box. By default, one will still get the original (non-Parser) versions of num_cmp and fun_cmp, but turning on the Parser-based versions will be much easier (just one line to change, or override on a course by course basis). There will be a companion change in the pg directories. Modified Files: -------------- webwork-modperl/conf: global.conf.dist Revision Data ------------- Index: global.conf.dist =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/conf/global.conf.dist,v retrieving revision 1.130 retrieving revision 1.131 diff -Lconf/global.conf.dist -Lconf/global.conf.dist -u -r1.130 -r1.131 --- conf/global.conf.dist +++ conf/global.conf.dist @@ -678,6 +678,10 @@ # Size in pixels of dynamically-generated images, i.e. graphs. $pg{specialPGEnvironmentVars}{onTheFlyImageSize} = 400, + +# To activate Parser-based versions of num_cmp and fun_cmp, change this +# value to 0. +$pg{specialPGEnvironmentVars}{useOldAnswerMacros} = 1; # Strings to insert at the start and end of the body of a problem # (at beginproblem() and ENDDOCUMENT) in various modes. More display modes @@ -726,6 +730,7 @@ [qw(Units)], [qw(VectorField)], [qw(Parser Value)], + [qw(Parser::Legacy)], [qw(Apache::Log)], ]; |
From: Gavin L. v. a. <we...@ma...> - 2005-08-25 17:01:19
|
Log Message: ----------- Intialize tth preamble variable to avoid undefined errors when running in formattedText mode. 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.182 retrieving revision 1.183 diff -Llib/WeBWorK/ContentGenerator/Problem.pm -Llib/WeBWorK/ContentGenerator/Problem.pm -u -r1.182 -r1.183 --- lib/WeBWorK/ContentGenerator/Problem.pm +++ lib/WeBWorK/ContentGenerator/Problem.pm @@ -328,7 +328,7 @@ } elsif ($displayMode eq "formattedText") { # read the TTH preamble, or use the cached copy passed in from the caller - my $tthPreamble; + my $tthPreamble=''; if (defined $$tthPreambleCache) { $tthPreamble = $$tthPreambleCache; } else { |
From: dpvc v. a. <we...@ma...> - 2005-08-25 03:39:31
|
Log Message: ----------- Fixed problem with Context's not properly initializing themselves with the WW environment parameters (like zeroLevelTol and useBaseTenLog). The copy method and the method that did an initialized copy used to be separate routines, but that turned out to be unnecessary, so they have now been merged (copy always tries to initialize the WW values when it can, and when the copied context doesn't already have them). The reason this is complicated is that a number contexts are set up before the global.conf data is available, so these can't be initialized with the WW values. Also, these default contexts are stored in the persistant processes, and we don't want to leave possibly course-specific values lying around in them, so whenever a context is selected, it is copied from the standard version and the WW parameters are inserted into it. The problem author can only modify the copy, not the original, so this is OK with mod_perl. The context's copy method now always tries to initialize (we used to have to call initCoopy to get a copy that has the WW parameters inserted). That turned out to cause trouble with code that handled contexts without the usual Context() command. Modified Files: -------------- pg/lib/Parser: Context.pm pg/lib/Value: Context.pm WeBWorK.pm Revision Data ------------- Index: Context.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser/Context.pm,v retrieving revision 1.13 retrieving revision 1.14 diff -Llib/Parser/Context.pm -Llib/Parser/Context.pm -u -r1.13 -r1.14 --- lib/Parser/Context.pm +++ lib/Parser/Context.pm @@ -16,7 +16,7 @@ # sub new { my $self = shift; my $class = ref($self) || $self; - my $context = $Value::defaultContext->initCopy; + my $context = $Value::defaultContext->copy; bless $context, $class; $context->{parser} = {%{$Parser::class}}; push(@{$context->{data}{values}},'parser'); @@ -105,7 +105,7 @@ $contextTable->{current} = $context; $Value::context = \$contextTable->{current}; } elsif (!defined($contextTable->{current})) { - $contextTable->{current} = $Parser::Context::Default::numericContext->initCopy; + $contextTable->{current} = $Parser::Context::Default::numericContext->copy; $Value::context = \$contextTable->{current}; } return $contextTable->{current}; @@ -121,7 +121,7 @@ return $context if $context; $context = $Parser::Context::Default::context{$name}; return unless $context; - return $context->initCopy; + return $context->copy; } # Index: WeBWorK.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/WeBWorK.pm,v retrieving revision 1.10 retrieving revision 1.11 diff -Llib/Value/WeBWorK.pm -Llib/Value/WeBWorK.pm -u -r1.10 -r1.11 --- lib/Value/WeBWorK.pm +++ lib/Value/WeBWorK.pm @@ -80,15 +80,16 @@ useBaseTenLog ); -sub Parser::Context::initCopy { +sub Parser::Context::copy { my $self = shift; - my $context = $self->copy(@_); + my $context = Value::Context::copy($self,@_); + return $context unless $Parser::installed; # only do WW initialization after parser is fully loaded return $context if $context->{WW} && scalar(keys %{$context->{WW}}) > 0; - $context->{WW} = {}; push @{$context->{data}{values}}, 'WW'; + my $envir = eval('\\%main::envir'); + return $context unless $envir && scalar(keys(%{$envir})) > 0; + my $ww = $context->{WW} = {}; push @{$context->{data}{values}}, 'WW'; return $context if $Value::_no_WeBWorK_; # hack for command-line debugging - return $context unless $Parser::installed; # only do WW initialization after parser is fully loaded - foreach my $x (@wwEvalFields) {$context->{WW}{$x} = eval('$main::envir{'.$x.'}');} - my $ww = $context->{WW}; + foreach my $x (@wwEvalFields) {$context->{WW}{$x} = $envir->{$x}} $context->flags->set( tolerance => $ww->{numRelPercentTolDefault} / 100, zeroLevel => $ww->{numZeroLevelDefault}, Index: Context.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Context.pm,v retrieving revision 1.7 retrieving revision 1.8 diff -Llib/Value/Context.pm -Llib/Value/Context.pm -u -r1.7 -r1.8 --- lib/Value/Context.pm +++ lib/Value/Context.pm @@ -81,11 +81,6 @@ return $context; } -# -# Make a copy with additional initialization -# (defined in subclasses) -# -sub initCopy {shift->copy(@_)} # # Make stringify produce TeX or regular strings |
From: dpvc v. a. <we...@ma...> - 2005-08-25 03:31:10
|
Log Message: ----------- Fixed log and log10 perl methods so that log obeys the useBaseTenLog flag, and log10 doesn't try to call main::log10, which is not available from within this preloaded package. The in- and outside the safe compartment stuff is very confusing, and functions that are not overloaded native perl functions seem to be not callable from the Value::Formula package, so the perl method now calls Parser::Function->call() directly (doesn't look pretty, but I've given up on that). Modified Files: -------------- pg/lib/Parser: Function.pm pg/lib/Parser/Function: numeric.pm Revision Data ------------- Index: Function.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser/Function.pm,v retrieving revision 1.14 retrieving revision 1.15 diff -Llib/Parser/Function.pm -Llib/Parser/Function.pm -u -r1.14 -r1.15 --- lib/Parser/Function.pm +++ lib/Parser/Function.pm @@ -281,9 +281,10 @@ # sub perl { my $self = shift; my $parens = shift; - my $fn = $self->{def}; my @p = (); + my $fn = $self->{def}; my @p = (); my $perl; foreach my $x (@{$self->{params}}) {push(@p,$x->perl)} - my $perl = ($fn->{perl}? $fn->{perl} : $self->{name}).'('.join(',',@p).')'; + if ($fn->{perl}) {$perl = $fn->{perl}.'('.join(',',@p).')'} + else {$perl = 'Parser::Function->call('.join(',',"'$self->{name}'",@p).')'} $perl = '('.$perl.')' if $parens == 1; return $perl; } Index: numeric.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser/Function/numeric.pm,v retrieving revision 1.5 retrieving revision 1.6 diff -Llib/Parser/Function/numeric.pm -Llib/Parser/Function/numeric.pm -u -r1.5 -r1.6 --- lib/Parser/Function/numeric.pm +++ lib/Parser/Function/numeric.pm @@ -69,6 +69,17 @@ return '\left|'.$self->{params}[0]->TeX.'\right|' if $self->{name} eq 'abs'; return $self->SUPER::TeX(@_); } +# +# Handle log (and useBaseTenLog) as a special case +# +sub perl { + my $self = shift; my $context; + $context = $self->{context} if ref($self); + $context = $$Value::context unless $context; + return $self->SUPER::perl + unless $self->{name} eq 'log' && $context->flag('useBaseTenLog'); + '(log('.$self->{params}[0]->perl.')/log(10))'; +} ######################################################################### |
From: dpvc v. a. <we...@ma...> - 2005-08-25 00:57:08
|
Log Message: ----------- Avoid undefined due_date when problem is viewed from the library browser. 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.181 retrieving revision 1.182 diff -Llib/WeBWorK/ContentGenerator/Problem.pm -Llib/WeBWorK/ContentGenerator/Problem.pm -u -r1.181 -r1.182 --- lib/WeBWorK/ContentGenerator/Problem.pm +++ lib/WeBWorK/ContentGenerator/Problem.pm @@ -181,7 +181,7 @@ $ce->{pg}->{options}->{showOldAnswers} = 0 unless ($authz->hasPermissions($userName, "can_show_old_answers_by_default")); # we are after the due date, so default to not showing it - $ce->{pg}->{options}->{showOldAnswers} = 0 if after($set->{due_date}); + $ce->{pg}->{options}->{showOldAnswers} = 0 if $set->{due_date} && after($set->{due_date}); } ################################################################################ |
From: dpvc v. a. <we...@ma...> - 2005-08-25 00:34:05
|
Log Message: ----------- In order to overcome an MSIE bug that affects jsMath, the call is changed from jsMath.ProcessBeforeShowing() to jsMath.wwProcess(), which is added by the jsMath-ww.js file. (This routine uses an onload handler for MSIE to put off the processing until MSIE can handle it properly, but allows all others to do it at the usual time.) Modified Files: -------------- pg/macros: PG.pl Revision Data ------------- Index: PG.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/PG.pl,v retrieving revision 1.25 retrieving revision 1.26 diff -Lmacros/PG.pl -Lmacros/PG.pl -u -r1.25 -r1.26 --- macros/PG.pl +++ macros/PG.pl @@ -516,7 +516,7 @@ # return results }; - $STRINGforOUTPUT .= '<SCRIPT> jsMath.ProcessBeforeShowing() </SCRIPT>' + $STRINGforOUTPUT .= '<SCRIPT> jsMath.wwProcess() </SCRIPT>' if ($main::envir{displayMode} eq 'HTML_jsMath'); if ($main::envir{displayMode} eq 'HTML_asciimath') { |
From: dpvc v. a. <we...@ma...> - 2005-08-25 00:30:49
|
Log Message: ----------- Updates to overcome yet another bug in MSIE. It turns out that MSIE can completely botch up the display if jsMath.ProcessBeforeShowing() is performed at the point in the web page that we have to put it, so we call wwProcess instead, and if it is in MSIE, we add an onload handler to call ProcessBeforeShowing. It's a hack, but it works. Modified Files: -------------- webwork-modperl/htdocs/jsMath: jsMath-controls.html jsMath-ww.js Revision Data ------------- Index: jsMath-controls.html =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/htdocs/jsMath/jsMath-controls.html,v retrieving revision 1.1 retrieving revision 1.2 diff -Lhtdocs/jsMath/jsMath-controls.html -Lhtdocs/jsMath/jsMath-controls.html -u -r1.1 -r1.2 --- htdocs/jsMath/jsMath-controls.html +++ htdocs/jsMath/jsMath-controls.html @@ -197,14 +197,13 @@ </TD></TR> <TR><TD ALIGN="CENTER" COLSPAN="2"> <INPUT TYPE="BUTTON" ID="jsMath.resolution" VALUE="Hi-Res Fonts for Printing" - STYLE="width:17em" onClick="jsMath.Controls.PrintResolution()"> + STYLE="width:16em" onClick="jsMath.Controls.PrintResolution()"> </TD></TR> <TR><TD HEIGHT="5"></TD></TR> -<TR><TD ALIGN="LEFT"> -<INPUT TYPE="BUTTON" VALUE="Options" ID="jsMath.opts" STYLE="width:8em" onClick="jsMath.Controls.Options()"> - +<TR VALIGN="TOP"><TD ALIGN="LEFT"> +<INPUT TYPE="BUTTON" VALUE="Options" ID="jsMath.opts" STYLE="width:7em" onClick="jsMath.Controls.Options()"> </TD><TD ALIGN="RIGHT"> -<INPUT TYPE="BUTTON" VALUE="Done" ID="jsMath.done" STYLE="width:8em" onClick="jsMath.Controls.Close()"> +<INPUT TYPE="BUTTON" VALUE="Done" ID="jsMath.done" STYLE="width:7em" onClick="jsMath.Controls.Close()"> </TD></TR> </TABLE></TD></TR> <TR><TD HEIGHT="10"></TD></TR> Index: jsMath-ww.js =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/htdocs/jsMath/jsMath-ww.js,v retrieving revision 1.1 retrieving revision 1.2 diff -Lhtdocs/jsMath/jsMath-ww.js -Lhtdocs/jsMath/jsMath-ww.js -u -r1.1 -r1.2 --- htdocs/jsMath/jsMath-ww.js +++ htdocs/jsMath/jsMath-ww.js @@ -28,6 +28,14 @@ src = src.replace(/jsMath-ww.js$/,'jsMath.js'); document.write('<SCRIPT SRC="'+src+'"></SCRIPT>'); } + }, + + wwProcess: function () { + if (jsMath.browser == 'MSIE') { + window.onload = function () {jsMath.ProcessBeforeShowing()} + } else { + jsMath.ProcessBeforeShowing(); + } } }; |
From: jj v. a. <we...@ma...> - 2005-08-24 20:21:29
|
Log Message: ----------- Make it so that by default, practice users don't see saved answers. That can be controlled in global.conf.dist. Also, default to not showing saved answers after the due date. In all cases, the Apply options button can adjust what you want. Also fixed a bug with showOldAnswers related to '', 0, and undef being similar in perl. Modified Files: -------------- webwork-modperl/conf: global.conf.dist webwork-modperl/lib/WeBWorK/ContentGenerator: Problem.pm Revision Data ------------- Index: global.conf.dist =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/conf/global.conf.dist,v retrieving revision 1.129 retrieving revision 1.130 diff -Lconf/global.conf.dist -Lconf/global.conf.dist -u -r1.129 -r1.130 --- conf/global.conf.dist +++ conf/global.conf.dist @@ -514,6 +514,7 @@ show_correct_answers_before_answer_date => $ta, show_solutions_before_answer_date => $ta, avoid_recording_answers => $ta, + can_show_old_answers_by_default => $student, check_answers_before_open_date => $ta, check_answers_after_open_date_with_attempts => $ta, check_answers_after_open_date_without_attempts => $guest, Index: Problem.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Problem.pm,v retrieving revision 1.180 retrieving revision 1.181 diff -Llib/WeBWorK/ContentGenerator/Problem.pm -Llib/WeBWorK/ContentGenerator/Problem.pm -u -r1.180 -r1.181 --- lib/WeBWorK/ContentGenerator/Problem.pm +++ lib/WeBWorK/ContentGenerator/Problem.pm @@ -174,6 +174,16 @@ sub after { return time >= $_[0] } sub between { my $t = time; return $t > $_[0] && $t < $_[1] } +# Reset the default in some cases +sub set_showOldAnswers_default { + my ($self, $ce, $userName, $authz, $set) = @_; + # this person should always default to 0 + $ce->{pg}->{options}->{showOldAnswers} = 0 + unless ($authz->hasPermissions($userName, "can_show_old_answers_by_default")); + # we are after the due date, so default to not showing it + $ce->{pg}->{options}->{showOldAnswers} = 0 if after($set->{due_date}); +} + ################################################################################ # output utilities ################################################################################ @@ -389,6 +399,8 @@ # obtain the merged set for $effectiveUser my $set = $db->getMergedSet($effectiveUserName, $setName); # checked + $self->set_showOldAnswers_default($ce, $userName, $authz, $set); + # gateway check here: we want to be sure that someone isn't trying to take # a GatewayQuiz through the regular problem/homework mechanism, thereby # circumventing the versioning, time limits, etc. @@ -543,8 +555,10 @@ # there is no way to override this. Probably this is ok for the last three options, but it was definitely not ok for showing # saved answers which is normally on, but you want to be able to turn it off! This section should be moved to ContentGenerator # so that you can set these options anywhere. We also need mechanisms for making them sticky. + # Note: ProblemSet and ProblemSets might set showOldAnswers to '', which + # needs to be treated as if it is not set. my %want = ( - showOldAnswers => defined($r->param("showOldAnswers")) ? $r->param("showOldAnswers") : $ce->{pg}->{options}->{showOldAnswers}, + showOldAnswers => (defined($r->param("showOldAnswers")) and $r->param("showOldAnswers") ne '') ? $r->param("showOldAnswers") : $ce->{pg}->{options}->{showOldAnswers}, showCorrectAnswers => $r->param("showCorrectAnswers") || $ce->{pg}->{options}->{showCorrectAnswers}, showHints => $r->param("showHints") || $ce->{pg}->{options}->{showHints}, showSolutions => $r->param("showSolutions") || $ce->{pg}->{options}->{showSolutions}, |
From: jj v. a. <we...@ma...> - 2005-08-24 19:40:52
|
Log Message: ----------- Fixed bug 753. The number of all users needs to be recomputed after action handlers in case some users were imported or deleted. Modified Files: -------------- webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor: UserList.pm Revision Data ------------- Index: UserList.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/UserList.pm,v retrieving revision 1.69 retrieving revision 1.70 diff -Llib/WeBWorK/ContentGenerator/Instructor/UserList.pm -Llib/WeBWorK/ContentGenerator/Instructor/UserList.pm -u -r1.69 -r1.70 --- lib/WeBWorK/ContentGenerator/Instructor/UserList.pm +++ lib/WeBWorK/ContentGenerator/Instructor/UserList.pm @@ -396,6 +396,7 @@ ########## retrieve possibly changed values for member fields #@allUserIDs = @{ $self->{allUserIDs} }; # do we need this one? + @allUserIDs = $db->listUsers; # recompute value in case some were added my @visibleUserIDs = @{ $self->{visibleUserIDs} }; my @prevVisibleUserIDs = @{ $self->{prevVisibleUserIDs} }; my @selectedUserIDs = @{ $self->{selectedUserIDs} }; |
From: dpvc v. a. <we...@ma...> - 2005-08-24 19:06:06
|
Log Message: ----------- Changed stringify to check for an answer that is zero by using the zeroLevelTol value directly (rather than use the fuzzy equality check for zero, which depends on the tolType in a bad way). 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.17 retrieving revision 1.18 diff -Llib/Value/Real.pm -Llib/Value/Real.pm -u -r1.17 -r1.18 --- lib/Value/Real.pm +++ lib/Value/Real.pm @@ -207,7 +207,7 @@ 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 = 0 if abs($n) < $$Value::context->flag('zeroLevelTol'); $n = "(".$n.")" if ($n < 0 || $n =~ m/E/i) && defined($prec) && $prec >= 1; return $n; } |
From: jj v. a. <we...@ma...> - 2005-08-24 16:56:59
|
Log Message: ----------- Fixed minor glitch where the toggle for show saved answers might be set wrong for the first time a student looks at a problem. Modified Files: -------------- webwork-modperl/lib/WeBWorK: ContentGenerator.pm Revision Data ------------- Index: ContentGenerator.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator.pm,v retrieving revision 1.146 retrieving revision 1.147 diff -Llib/WeBWorK/ContentGenerator.pm -Llib/WeBWorK/ContentGenerator.pm -u -r1.146 -r1.147 --- lib/WeBWorK/ContentGenerator.pm +++ lib/WeBWorK/ContentGenerator.pm @@ -1334,7 +1334,9 @@ } if (exists $options_to_show{showOldAnswers}) { - my $curr_showOldAnswers = $self->r->param("showOldAnswers"); + # Note, 0 is a legal value, so we can't use || in setting this + my $curr_showOldAnswers = defined($self->r->param("showOldAnswers")) ? + $self->r->param("showOldAnswers") : $self->r->ce->{pg}->{options}->{showOldAnswers}; $result .= "Show saved answers?"; $result .= CGI::br(); $result .= CGI::radio_group( |
From: dpvc v. a. <we...@ma...> - 2005-08-24 11:43:34
|
Log Message: ----------- Made relative tolerances the default for when the tolType is not one of 'relative' or 'absolute'. 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.10 retrieving revision 1.11 diff -Llib/Parser/Legacy/PGanswermacros.pl -Llib/Parser/Legacy/PGanswermacros.pl -u -r1.10 -r1.11 --- lib/Parser/Legacy/PGanswermacros.pl +++ lib/Parser/Legacy/PGanswermacros.pl @@ -1044,7 +1044,7 @@ # Hack to fix up exponential notation in correct answer # (e.g., perl will pass .0000001 as 1e-07). # - $correctAnswer = Value::Real->new($correctAnswer)->string + $correctAnswer = uc($correctAnswer) if $correctAnswer =~ m/e/ && Value::isNumber($correctAnswer); # @@ -1086,15 +1086,15 @@ # # Set the tolerances # - if ($num_params{tolType} eq 'relative') { + if ($num_params{tolType} eq 'absolute') { $context->flags->set( - tolerance => .01*$num_params{tolerance}, - tolType => 'relative', + tolerance => $num_params{tolerance}, + tolType => 'absolute', ); } else { $context->flags->set( - tolerance => $num_params{tolerance}, - tolType => 'absolute', + tolerance => .01*$num_params{tolerance}, + tolType => 'relative', ); } $context->flags->set( |
From: jj v. a. <we...@ma...> - 2005-08-24 02:21:45
|
Log Message: ----------- Removed initialize function which did nothing. Moved the actual assignments/unassignments to pre_header_initialize so they could use the messaging system. Modified Files: -------------- webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor: Assigner.pm Revision Data ------------- Index: Assigner.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/Assigner.pm,v retrieving revision 1.32 retrieving revision 1.33 diff -Llib/WeBWorK/ContentGenerator/Instructor/Assigner.pm -Llib/WeBWorK/ContentGenerator/Instructor/Assigner.pm -u -r1.32 -r1.33 --- lib/WeBWorK/ContentGenerator/Instructor/Assigner.pm +++ lib/WeBWorK/ContentGenerator/Instructor/Assigner.pm @@ -28,16 +28,50 @@ use CGI qw(); use WeBWorK::HTML::ScrollingRecordList qw/scrollingRecordList/; -sub initialize { +sub pre_header_initialize { my ($self) = @_; my $r = $self->r; + my $db = $r->db; my $authz = $r->authz; - + my $ce = $r->ce; my $user = $r->param('user'); - # Check permissions - return unless ($authz->hasPermissions($user, "access_instructor_tools")); - return unless ($authz->hasPermissions($user, "assign_problem_sets")); + # Permissions dealt with in the body + return "" unless $authz->hasPermissions($user, "access_instructor_tools"); + return "" unless $authz->hasPermissions($user, "assign_problem_sets"); + + my @selected_users = $r->param("selected_users"); + my @selected_sets = $r->param("selected_sets"); + + if (defined $r->param("assign") || defined $r->param("unassign")) { + if (@selected_users && @selected_sets) { + my @results; # This is not used? + if(defined $r->param("assign")) { + $self->assignSetsToUsers(\@selected_sets, \@selected_users); + $self->addgoodmessage('All assignments were made successfully.'); + } + if (defined $r->param("unassign")) { + if(defined $r->param('unassignFromAllSafety') and $r->param('unassignFromAllSafety')==1) { + $self->unassignSetsFromUsers(\@selected_sets, \@selected_users) if(defined $r->param("unassign")); + $self->addgoodmessage('All unassignments were made successfully.'); + } else { # asked for unassign, but no safety radio toggle + $self->addbadmessage('Unassignments were not done. You need to both click to "Allow unassign" and click on the Unassign button.'); + } + } + + if (@results) { # Can't get here? + $self->addbadmessage( + "The following error(s) occured while assigning:". + CGI::ul(CGI::li(\@results)) + ); + } + } else { + $self->addbadmessage("You must select one or more users below.") + unless @selected_users; + $self->addbadmessage("You must select one or more sets below.") + unless @selected_sets; + } + } } sub body { @@ -91,39 +125,6 @@ my @globalSetIDs = $db->listGlobalSets; my @GlobalSets = $db->getGlobalSets(@globalSetIDs); - my @selected_users = $r->param("selected_users"); - my @selected_sets = $r->param("selected_sets"); - - if (defined $r->param("assign") || defined $r->param("unassign")) { - if (@selected_users && @selected_sets) { - my @results; # This is not used? - if(defined $r->param("assign")) { - $self->assignSetsToUsers(\@selected_sets, \@selected_users); - print CGI::div({class=>"ResultsWithoutError"},CGI::p('All assignments were made successfully.')); - } - if (defined $r->param("unassign")) { - if(defined $r->param('unassignFromAllSafety') and $r->param('unassignFromAllSafety')==1) { - $self->unassignSetsFromUsers(\@selected_sets, \@selected_users) if(defined $r->param("unassign")); - print CGI::div({class=>"ResultsWithoutError"},CGI::p('All unassignments were made successfully.')); - } else { # asked for unassign, but no safety radio toggle - print CGI::div({class=>"ResultsWithError"},CGI::p('Unassignments were not done. You need to both click to "Allow unassign" and click on the Unassign button.')); - } - } - - if (@results) { # Can't get here? - print CGI::div({class=>"ResultsWithError"}, - CGI::p("The following error(s) occured while assigning:"), - CGI::ul(CGI::li(\@results)), - ); - } - } else { - print CGI::div({class=>"ResultsWithError"}, - @selected_users ? () : CGI::p("You must select one or more users below."), - @selected_sets ? () : CGI::p("You must select one or more sets below."), - ); - } - } - my $scrolling_user_list = scrollingRecordList({ name => "selected_users", request => $r, |
From: jj v. a. <we...@ma...> - 2005-08-24 01:51:11
|
Log Message: ----------- Added safety toggle for unassignment on this page (like other places where sets can be unassigned). Modified Files: -------------- webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor: Assigner.pm Revision Data ------------- Index: Assigner.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/Assigner.pm,v retrieving revision 1.31 retrieving revision 1.32 diff -Llib/WeBWorK/ContentGenerator/Instructor/Assigner.pm -Llib/WeBWorK/ContentGenerator/Instructor/Assigner.pm -u -r1.31 -r1.32 --- lib/WeBWorK/ContentGenerator/Instructor/Assigner.pm +++ lib/WeBWorK/ContentGenerator/Instructor/Assigner.pm @@ -97,21 +97,24 @@ if (defined $r->param("assign") || defined $r->param("unassign")) { if (@selected_users && @selected_sets) { my @results; # This is not used? - $self->assignSetsToUsers(\@selected_sets, \@selected_users) if defined $r->param("assign"); - $self->unassignSetsFromUsers(\@selected_sets, \@selected_users) if defined $r->param("unassign"); + if(defined $r->param("assign")) { + $self->assignSetsToUsers(\@selected_sets, \@selected_users); + print CGI::div({class=>"ResultsWithoutError"},CGI::p('All assignments were made successfully.')); + } + if (defined $r->param("unassign")) { + if(defined $r->param('unassignFromAllSafety') and $r->param('unassignFromAllSafety')==1) { + $self->unassignSetsFromUsers(\@selected_sets, \@selected_users) if(defined $r->param("unassign")); + print CGI::div({class=>"ResultsWithoutError"},CGI::p('All unassignments were made successfully.')); + } else { # asked for unassign, but no safety radio toggle + print CGI::div({class=>"ResultsWithError"},CGI::p('Unassignments were not done. You need to both click to "Allow unassign" and click on the Unassign button.')); + } + } if (@results) { # Can't get here? print CGI::div({class=>"ResultsWithError"}, CGI::p("The following error(s) occured while assigning:"), CGI::ul(CGI::li(\@results)), ); - } else { - my $happymessage= 'All assignments were made successfully.'; - $happymessage='All unassignments were made successfully.' - if defined $r->param("unassign"); - print CGI::div({class=>"ResultsWithoutError"}, - CGI::p($happymessage), - ); } } else { print CGI::div({class=>"ResultsWithError"}, @@ -174,6 +177,7 @@ -value => "Unassign selected sets from selected users", -style => "width: 45ex", ), + CGI::radio_group(-name=>"unassignFromAllSafety", -values=>[0,1], -default=>0, -labels=>{0=>'Assignments only', 1=>'Allow unassign'}), ), ), ), |
From: jj v. a. <we...@ma...> - 2005-08-24 01:33:45
|
Log Message: ----------- Hopefully fixed the logic of setting numerical tolerance defaults. Modified Files: -------------- pg/macros: extraAnswerEvaluators.pl Revision Data ------------- Index: extraAnswerEvaluators.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/extraAnswerEvaluators.pl,v retrieving revision 1.12 retrieving revision 1.13 diff -Lmacros/extraAnswerEvaluators.pl -Lmacros/extraAnswerEvaluators.pl -u -r1.12 -r1.13 --- macros/extraAnswerEvaluators.pl +++ macros/extraAnswerEvaluators.pl @@ -189,17 +189,21 @@ $context = $Parser::Context::Default::context{'Complex'}; } $options{tolType} = $options{tolType} || 'relative'; - $options{tolerance} = $options{tolerance} || $options{tol} || - $options{reltol} || $options{relTol} || $options{abstol} || 1; + $options{tolType} = 'absolute' if defined($options{tol}); $options{zeroLevel} = $options{zeroLevel} || $options{zeroLevelTol} || $main::numZeroLevelTolDefault; - if ($options{tolType} eq 'absolute' or defined($options{tol}) - or defined($options{abstol})) { + if ($options{tolType} eq 'absolute' or defined($options{abstol})) { + $options{tolerance} = $options{tolerance} || $options{tol} || + $options{reltol} || $options{relTol} || $options{abstol} || + $main::numAbsTolDefault; $context->flags->set( tolerance => $options{tolerance}, tolType => 'absolute', ); } else { + $options{tolerance} = $options{tolerance} || $options{tol} || + $options{reltol} || $options{relTol} || $options{abstol} || + $main::numRelPercentTolDefault; $context->flags->set( tolerance => .01*$options{tolerance}, tolType => 'relative', |
From: jj v. a. <we...@ma...> - 2005-08-23 23:58:28
|
Log Message: ----------- Fixed problem with display_matrix_mm so its behaviour matches its documentation. This may break problems which relied on its previous incorrect behavior. Modified Files: -------------- pg/macros: PGmatrixmacros.pl Revision Data ------------- Index: PGmatrixmacros.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/PGmatrixmacros.pl,v retrieving revision 1.12 retrieving revision 1.13 diff -Lmacros/PGmatrixmacros.pl -Lmacros/PGmatrixmacros.pl -u -r1.12 -r1.13 --- macros/PGmatrixmacros.pl +++ macros/PGmatrixmacros.pl @@ -519,8 +519,12 @@ if(ref($element) eq 'Fraction') { $element= $element->print_inline(); } - $out .= '\\mbox{'."$element".'}'; - $out .= '}' if ($colcount == $opts{'box'}->[1] and $opts{'cnt'} == $opts{'box'}->[0]); + if($opts{'force_tex'}) { + $out .= "$element"; + } else { + $out .= '\\mbox{'."$element".'}'; + } + $out .= '}' if ($colcount == $opts{'box'}->[1] and $opts{'cnt'} == $opts{'box'}->[0]); $out .= " &"; } chop($out); # remove last & |