From: Mike G. v. a. <we...@ma...> - 2009-06-25 23:41:20
|
Log Message: ----------- Syncing pg2.4.7 with pg HEAD on 6/25/2009 Tags: ---- rel-2-4-patches Modified Files: -------------- pg: LICENSE README pg/lib: Applet.pm pg/lib/Parser: Function.pm pg/lib/Parser/Legacy: LimitedNumeric.pm pg/lib/Value: AnswerChecker.pm Complex.pm Context.pm Formula.pm Union.pm pg/lib/WeBWorK/PG: Translator.pm pg/macros: AppletObjects.pl PGbasicmacros.pl contextInequalities.pl contextLimitedPolynomial.pl contextPiecewiseFunction.pl contextString.pl dangerousMacros.pl parserFormulaUpToConstant.pl parserImplicitEquation.pl parserPopUp.pl parserRadioButtons.pl Revision Data ------------- Index: README =================================================================== RCS file: /webwork/cvs/system/pg/README,v retrieving revision 1.2.4.2.2.1 retrieving revision 1.2.4.2.2.2 diff -LREADME -LREADME -u -r1.2.4.2.2.1 -r1.2.4.2.2.2 --- README +++ README @@ -1,6 +1,10 @@ WeBWorK Program Generation Language - Version 2.4.5 - - Copyright 2000-2007, The WeBWorK Project + Version 2.4.x + Branch: rel-2-4-patche + + + http://webwork.maa.org/wiki/Release_notes_for_WeBWorK_2.4.7 + + Copyright 2000-2009, The WeBWorK Project All rights reserved. Index: LICENSE =================================================================== RCS file: /webwork/cvs/system/pg/LICENSE,v retrieving revision 1.2.4.2.2.1 retrieving revision 1.2.4.2.2.2 diff -LLICENSE -LLICENSE -u -r1.2.4.2.2.1 -r1.2.4.2.2.2 --- LICENSE +++ LICENSE @@ -1,8 +1,8 @@ WeBWorK - Program Generation Language - Version 2.4.5 + Online Homework Delivery System + Version 2.4.7 - Copyright 2000-2008, The WeBWorK Project + Copyright 2000-2009, The WeBWorK Project All rights reserved. This program is free software; you can redistribute it and/or modify Index: Function.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser/Function.pm,v retrieving revision 1.18.6.2.2.1 retrieving revision 1.18.6.2.2.2 diff -Llib/Parser/Function.pm -Llib/Parser/Function.pm -u -r1.18.6.2.2.1 -r1.18.6.2.2.2 --- lib/Parser/Function.pm +++ lib/Parser/Function.pm @@ -256,7 +256,9 @@ foreach my $x (@{$self->{params}}) {push(@pstr,$x->string)} $string = ($self->{def}{string} || $self->{name})."$power".'('.join(',',@pstr).')'; $string = $self->addParens($string) - if (defined($precedence) and $precedence > $fn_precedence) || $showparens; + if $showparens eq 'all' or $showparens eq 'extra' or + (defined($precedence) and $precedence > $fn_precedence) or + (defined($precedence) and $precedence == $fn_precedence and $showparens eq 'same'); return $string; } @@ -275,7 +277,9 @@ if ($fn->{braceTeX}) {$TeX = $name.'{'.join(',',@pstr).'}'} else {$TeX = $name."$power".'\!\left('.join(',',@pstr).'\right)'} $TeX = '\left('.$TeX.'\right)' - if (defined($precedence) and $precedence > $fn_precedence) or $showparens; + if $showparens eq 'all' or $showparens eq 'extra' or + (defined($precedence) and $precedence > $fn_precedence) or + (defined($precedence) and $precedence == $fn_precedence and $showparens eq 'same'); return $TeX; } Index: Complex.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Complex.pm,v retrieving revision 1.18.6.2.2.1 retrieving revision 1.18.6.2.2.2 diff -Llib/Value/Complex.pm -Llib/Value/Complex.pm -u -r1.18.6.2.2.1 -r1.18.6.2.2.2 --- lib/Value/Complex.pm +++ lib/Value/Complex.pm @@ -336,8 +336,7 @@ $a->{format} = $b->{format} = $format if defined $format; my $bi = 'i'; return $a->$method($equation) if $b == 0; - $bi = CORE::abs($b)->with(format=>$format)->$method($equation,1) . 'i' - if CORE::abs($b) !~ m/^1(\.0*)?$/; + $bi = CORE::abs($b)->with(format=>$format)->$method($equation,1) . 'i' if CORE::abs($b) !~ m/^1(\.0*)?$/; $bi = '-' . $bi if $b < 0; return $bi if $a == 0; $bi = '+' . $bi if $b > 0; Index: AnswerChecker.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/AnswerChecker.pm,v retrieving revision 1.91.2.2.2.4 retrieving revision 1.91.2.2.2.5 diff -Llib/Value/AnswerChecker.pm -Llib/Value/AnswerChecker.pm -u -r1.91.2.2.2.4 -r1.91.2.2.2.5 --- lib/Value/AnswerChecker.pm +++ lib/Value/AnswerChecker.pm @@ -30,9 +30,9 @@ # Internal use. # Set default flags for the answer checker in this object -# showTypeWarnings => 1 -# showEqualErrors => 1 -# ignoreStrings => 1 +# showTypeWarnings => 1 +# showEqualErrors => 1 +# ignoreStrings => 1 # studentsMustReduceUnions => 1 # showUnionReduceWarnings => 1 # @@ -241,6 +241,7 @@ return eval {$self == $other} unless ref($ans->{checker}) eq 'CODE'; my @equal = eval {&{$ans->{checker}}($self,$other,$ans,$nth,@_)}; if (!defined($equal) && $@ ne '' && (!$context->{error}{flag} || $ans->{showAllErrors})) { + $nth = "" if ref($nth) eq 'AnswerHash'; $context->setError(["<I>An error occurred while checking your$nth answer:</I>\n". '<DIV STYLE="margin-left:1em">%s</DIV>',$@],'',undef,undef,$CMP_ERROR); warn "Please inform your instructor that an error occurred while checking your answer"; @@ -415,12 +416,13 @@ $self->{format_options} = [%options] unless $self->{format_options}; my ($open,$close,$sep) = ($options{open},$options{close},$options{sep}); my ($rows,$cols) = (scalar(@{$array}),scalar(@{$array->[0]})); - my $tex = ""; + my $tex = ""; my @rows = (); $open = '\\'.$open if $open =~ m/[{}]/; $close = '\\'.$close if $close =~ m/[{}]/; $tex .= '\(\left'.$open; $tex .= '\setlength{\arraycolsep}{2pt}', $sep = '\,'.$sep if $sep; $tex .= '\begin{array}{'.('c'x$cols).'}'; - foreach my $i (0..$rows-1) {$tex .= join($sep.'&',@{$array->[$i]}).'\cr'."\n"} + foreach my $i (0..$rows-1) {push(@rows,join($sep.'&',@{$array->[$i]}))} + $tex .= join('\cr'."\n",@rows); $tex .= '\end{array}\right'.$close.'\)'; return $tex; } @@ -1646,6 +1648,7 @@ my $self = shift; my $ans = shift; $ans->{_filter_name} = "produce_equivalence_message"; return $ans if $ans->{ans_message}; # don't overwrite other messages + return $ans unless defined($ans->{prev_ans}); # if prefilters are erased, don't do this check my $context = $self->context; $ans->{prev_formula} = Parser::Formula($context,$ans->{prev_ans}); if (defined($ans->{prev_formula}) && defined($ans->{student_formula})) { @@ -1784,7 +1787,7 @@ my @P = (map {(scalar(@{$_}) == 1)? $_->[0]: $self->Package("Point")->make(@{$_})} @{$self->{test_points}}); my @i = sort {$P[$a] <=> $P[$b]} (0..$#P); foreach $p (@P) {if (Value::isValue($p) && $p->length > 2) {$p = $p->string; $p =~ s|,|,<br />|g}} - my $zeroLevelTol = $self->getFlag('zeroLevelTol'); + my $zeroLevelTol = $self->{context}{flags}{zeroLevelTol}; $self->{context}{flags}{zeroLevelTol} = 0; # always show full resolution in the tables below my $names = join(',',@names); $names = '('.$names.')' if scalar(@names) > 1; Index: Formula.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Formula.pm,v retrieving revision 1.43.2.2.2.2 retrieving revision 1.43.2.2.2.3 diff -Llib/Value/Formula.pm -Llib/Value/Formula.pm -u -r1.43.2.2.2.2 -r1.43.2.2.2.3 --- lib/Value/Formula.pm +++ lib/Value/Formula.pm @@ -188,7 +188,7 @@ # situations where this is a problem. # if ($l->AdaptParameters($r,$self->{context}->variables->parameters)) { - my $avalues = $l->{test_adapt}; + my $avalues = $l->{test_adapt}; my $tolerance = $self->getFlag('tolerance',1E-4); my $isRelative = $self->getFlag('tolType','relative') eq 'relative'; my $zeroLevel = $self->getFlag('zeroLevel',1E-14); @@ -220,6 +220,17 @@ } # +# Inherit should make sure the tree is copied +# (so it's nodes point to the correct equation, for one thing) +# +sub inherit { + my $self = shift; + $self = $self->SUPER::inherit(@_); + $self->{tree} = $self->{tree}->copy($self); + return $self; +} + +# # Don't inherit test values or adapted values, or other temporary items # sub noinherit { @@ -446,10 +457,15 @@ foreach my $i (0..$d-1) { my @a = (); my @p = @{$p->[$i]}; foreach my $j (0..$d-1) { - $P[$j] = 1; push(@a,(&$f(@p,@P)-$v->[$i])->value); - $P[$j] = 0; + $P[$j] = 1; + my $y = eval {&$f(@p,@P)}; + $l->Error(["Can't evaluate correct answer at adapted point (%s)",join(",",@$p,@P)]) + unless defined $y; + push(@a,($y-$v->[$i])->value); + $P[$j] = 0; } - push @A, [@a]; push @b, [(&$F(@p,@P)-$v->[$i])->value]; + my $y = eval {&$F(@p,@P)}; return unless defined $y; + push @A, [@a]; push @b, [($y-$v->[$i])->value]; } # # Use MatrixReal1.pm to solve system of linear equations @@ -458,27 +474,29 @@ my $B = MatrixReal1->new($d,1); $B->[0] = \@b; ($M,$B) = $M->normalize($B); $M = $M->decompose_LR; - if (($D,$B,$M) = $M->solve_LR($B)) { - if ($D == 0) { - # - # Get parameter values and recompute the points using them - # - my @a; my $i = 0; my $max = $l->getFlag('max_adapt',1E8); - foreach my $row (@{$B->[0]}) { - if (abs($row->[0]) > $max) { - $max = Value::makeValue($max); $row->[0] = Value::makeValue($row->[0]); - $l->Error(["Constant of integration is too large: %s\n(maximum allowed is %s)", - $row->[0]->string,$max->string]) if $params[$i] eq 'C0' or $params[$i] eq 'n00'; - $l->Error(["Adaptive constant is too large: %s = %s\n(maximum allowed is %s)", - $params[$i],$row->[0]->string,$max->string]); - } - push @a, $row->[0]; $i++; - } - my $context = $l->context; - foreach my $i (0..$#a) {$context->{variables}{$params[$i]}{value} = $a[$i]} - $l->{parameters} = [@a]; - $l->createAdaptedValues; - return 1; + if (abs($M->det_LR) > 1E-6) { + if (($D,$B,$M) = $M->solve_LR($B)) { + if ($D == 0) { + # + # Get parameter values and recompute the points using them + # + my @a; my $i = 0; my $max = $l->getFlag('max_adapt',1E8); + foreach my $row (@{$B->[0]}) { + if (abs($row->[0]) > $max) { + $max = Value::makeValue($max); $row->[0] = Value::makeValue($row->[0]); + $l->Error(["Constant of integration is too large: %s\n(maximum allowed is %s)", + $row->[0]->string,$max->string]) if $params[$i] eq 'C0' or $params[$i] eq 'n00'; + $l->Error(["Adaptive constant is too large: %s = %s\n(maximum allowed is %s)", + $params[$i],$row->[0]->string,$max->string]); + } + push @a, $row->[0]; $i++; + } + my $context = $l->context; + foreach my $i (0..$#a) {$context->{variables}{$params[$i]}{value} = $a[$i]} + $l->{parameters} = [@a]; + $l->createAdaptedValues; + return 1; + } } } } Index: Context.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Context.pm,v retrieving revision 1.10.6.2.2.2 retrieving revision 1.10.6.2.2.3 diff -Llib/Value/Context.pm -Llib/Value/Context.pm -u -r1.10.6.2.2.2 -r1.10.6.2.2.3 --- lib/Value/Context.pm +++ lib/Value/Context.pm @@ -142,8 +142,8 @@ while ($message && $error->{msg}{$message}) {$message = $error->{msg}{$message}} while ($more && $error->{msg}{$more}) {$more = $error->{msg}{$more}} $message = sprintf($message,@args) if scalar(@args) > 0; - $message .= sprintf($more,$pos->[0]+1) if $more; while ($message && $error->{msg}{$message}) {$message = $error->{msg}{$message}} + $message .= sprintf($more,$pos->[0]+1) if $more; $message = &{$error->{convert}}($message) if defined $error->{convert}; $error->{message} = $message; $error->{string} = $string; Index: Union.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Union.pm,v retrieving revision 1.24.6.2.2.1 retrieving revision 1.24.6.2.2.2 diff -Llib/Value/Union.pm -Llib/Value/Union.pm -u -r1.24.6.2.2.1 -r1.24.6.2.2.2 --- lib/Value/Union.pm +++ lib/Value/Union.pm @@ -3,7 +3,6 @@ package Value::Union; my $pkg = 'Value::Union'; - qw(Value); use strict; no strict "refs"; our @ISA = qw(Value); Index: LimitedNumeric.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser/Legacy/LimitedNumeric.pm,v retrieving revision 1.3.6.1 retrieving revision 1.3.6.1.2.1 diff -Llib/Parser/Legacy/LimitedNumeric.pm -Llib/Parser/Legacy/LimitedNumeric.pm -u -r1.3.6.1 -r1.3.6.1.2.1 --- lib/Parser/Legacy/LimitedNumeric.pm +++ lib/Parser/Legacy/LimitedNumeric.pm @@ -120,7 +120,7 @@ $context = $context->copy; $Parser::Context::Default::context{'LimitedNumeric-StrictFraction'} = $context; Parser::Number::NoDecimals($context); -$context->{name} = "LimitedNumeric-StrictFractions"; +$context->{name} = "LimitedNumeric-StrictFraction"; ###################################################################### Index: Applet.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Applet.pm,v retrieving revision 1.12.2.1.2.2 retrieving revision 1.12.2.1.2.3 diff -Llib/Applet.pm -Llib/Applet.pm -u -r1.12.2.1.2.2 -r1.12.2.1.2.3 --- lib/Applet.pm +++ lib/Applet.pm @@ -127,8 +127,7 @@ ---------------------------------------------------------------------------- - List of accessor methods made available by the (perl) FlashApplet class. - They are also the names of the instance variables in + List of accessor methods made available by the FlashApplet class: Usage: $current_value = $applet->method(new_value or empty) These can also be set when creating the class -- for exampe: $applet = new FlashApplet( @@ -200,7 +199,13 @@ returnFieldName -- (deprecated) synonmym for answerBoxAlias - debugMode (default: 0) in debug mode several alerts mark progress through the procedure of calling the applet + debugMode (default: 0) for debugMode==1 the answerBox and the box preserving the applet state + between questions are made visible along with some buttons for manually getting the state of + the applet and setting the state of the applet. + + for debugMode==2, in addition to the answerBox and stateBox there are several alerts + which mark progress through the procedures of calling the applet. Useful for troubleshooting + where in the chain of command a communication failure occurs Methods: Index: Translator.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/WeBWorK/PG/Translator.pm,v retrieving revision 1.18.2.2.2.3 retrieving revision 1.18.2.2.2.4 diff -Llib/WeBWorK/PG/Translator.pm -Llib/WeBWorK/PG/Translator.pm -u -r1.18.2.2.2.3 -r1.18.2.2.2.4 --- lib/WeBWorK/PG/Translator.pm +++ lib/WeBWorK/PG/Translator.pm @@ -908,7 +908,6 @@ =cut - my ($PG_PROBLEM_TEXT_REF, $PG_HEADER_TEXT_REF, $PG_ANSWER_HASH_REF, $PG_FLAGS_REF) =$safe_cmpt->reval(" $evalString"); @@ -1621,26 +1620,17 @@ } -sub original_preprocess_code { - my $evalString = shift; - # BEGIN_TEXT and END_TEXT must occur on a line by themselves. - $evalString =~ s/\n\s*END_TEXT[\s;]*\n/\nEND_TEXT\n/g; - $evalString =~ s/\n\s*BEGIN_TEXT[\s;]*\n/\nTEXT\(EV3\(<<'END_TEXT'\)\);\n/g; - $evalString =~ s/ENDDOCUMENT.*/ENDDOCUMENT();/s; # remove text after ENDDOCUMENT - $evalString =~ s/\\/\\\\/g; # \ can't be used for escapes because of TeX conflict - $evalString =~ s/~~/\\/g; # use ~~ as escape instead, use # for comments - $evalString; -} + sub default_preprocess_code { my $evalString = shift; # BEGIN_TEXT and END_TEXT must occur on a line by themselves. - $evalString =~ s/\n\s*END_TEXT[\s;]*\n/\nEND_TEXT\n/g; - $evalString =~ s/\n\s*END_SOLUTION[\s;]*\n/\nEND_SOLUTION\n/g; - $evalString =~ s/\n\s*END_HINT[\s;]*\n/\nEND_HINT\n/g; - $evalString =~ s/\n\s*BEGIN_TEXT[\s;]*\n/\nTEXT\(EV3P\(<<'END_TEXT'\)\);\n/g; - $evalString =~ s/\n\s*BEGIN_SOLUTION[\s;]*\n/\nSOLUTION\(EV3P\(<<'END_SOLUTION'\)\);\n/g; - $evalString =~ s/\n\s*BEGIN_HINT[\s;]*\n/\nHINT\(EV3P\(<<'END_HINT'\)\);\n/g; + $evalString =~ s/^[ \t]*END_TEXT[ \t;]*$/END_TEXT/gm; + $evalString =~ s/^[ \t]*END_SOLUTION[ \t;]*$/END_SOLUTION/mg; + $evalString =~ s/^[ \t]*END_HINT[ \t;]*$/END_HINT/mg; + $evalString =~ s/^[ \t]*BEGIN_TEXT[ \t;]*$/TEXT\(EV3P\(<<'END_TEXT'\)\);/mg; + $evalString =~ s/^[ \t]*BEGIN_SOLUTION[ \t;]*$/SOLUTION\(EV3P\(<<'END_SOLUTION'\)\);/mg; + $evalString =~ s/^[ \t]*BEGIN_HINT[ \t;]*$/HINT\(EV3P\(<<'END_HINT'\)\);/mg; $evalString =~ s/ENDDOCUMENT.*/ENDDOCUMENT();/s; # remove text after ENDDOCUMENT $evalString =~ s/\\/\\\\/g; # \ can't be used for escapes because of TeX conflict Index: parserRadioButtons.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/parserRadioButtons.pl,v retrieving revision 1.2.2.2.2.1 retrieving revision 1.2.2.2.2.2 diff -Lmacros/parserRadioButtons.pl -Lmacros/parserRadioButtons.pl -u -r1.2.2.2.2.1 -r1.2.2.2.2.2 --- macros/parserRadioButtons.pl +++ macros/parserRadioButtons.pl @@ -2,12 +2,12 @@ # WeBWorK Online Homework Delivery System # Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ -# +# # This program is free software; you can redistribute it and/or modify it under # the terms of either: (a) the GNU General Public License as published by the # Free Software Foundation; either version 2, or (at your option) any later # version, or (b) the "Artistic License" which comes with this package. -# +# # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the @@ -158,7 +158,7 @@ Value::Error("A RadioButton's second argument should be the correct button choice") unless defined($value) && $value ne ""; my $context = Parser::Context->getCopy("String"); - my %choiceHash = $self->choiceHash(1); + my %choiceHash = $self->choiceHash; $context->strings->add(map {$_=>{}} (keys %choiceHash)); $value = $self->correctChoice($value); $self = bless $context->Package("String")->new($context,$value)->with(choices => $choices, %options), $class; @@ -166,18 +166,19 @@ return $self; } -# +# # Given a choice, a label, or an index into the choices array, # return the label. -# +# sub findChoice { my $self = shift; my $value = shift; my $index = $self->Index($value); foreach my $i (0..scalar(@{$self->{choices}})-1) { my $label = $self->{labels}[$i]; my $choice = $self->{choices}[$i]; - $label = $self->makeLabel($choice) unless defined $label; + $label = $choice unless defined $label; return $label if $label eq $value || $index == $i || $choice eq $value; } + return undef; } # @@ -197,15 +198,10 @@ # ans_radio_buttons() routine # sub choiceHash { - my $self = shift; my $noChecked = shift; - my @radio = (); - my $index = $self->Index($self->{checked}); - my $checked = $self->{checked}; $checked = "" unless defined $checked; - if ($noChecked) {$checked = ""; $index = -1} + my $self = shift; my @radio = (); my %labels; foreach my $i (0..scalar(@{$self->{choices}})-1) { my $label = $self->{labels}[$i]; my $choice = $self->{choices}[$i]; - $label = $self->makeLabel($choice) unless defined $label; - $label = "%$label" if $label eq $checked || $index == $i || $choice eq $checked; + $label = $choice unless defined $label; push(@radio, $label,$choice); } return @radio; @@ -213,15 +209,14 @@ # # Create a label for the answer, either using the labels -# provided by the user, or by creating one from the answer +# provided by the author, or by creating one from the answer # string (restrict its length so that the results table # will not be overflowed). # -sub makeLabel { +sub labelText { my $self = shift; my $choice = shift; return $choice if length($choice) < $self->{maxLabelSize}; - my @words = split(/\b/,$choice); - my ($s,$e) = ('',''); + my @words = split(/\b/,$choice); my ($s,$e) = ('',''); do {$s .= shift(@words); $e = pop(@words) . $e} while length($s) + length($e) + 15 < $self->{maxLabelSize} && scalar(@words); return $s . " ... " . $e; @@ -240,8 +235,7 @@ # Print the JavaScript needed for uncheckable radio buttons # sub JavaScript { - return if $main::displayMode eq 'TeX'; - return if $jsPrinted; + return if $jsPrinted || $main::displayMode eq 'TeX'; main::TEXT( "\n<script>\n" . "if (window.ww == null) {var ww = {}}\n" . @@ -263,7 +257,7 @@ "}\n". "</script>\n" ); - $jsSPrinted = 1; + $jsPrinted = 1; } sub makeUncheckable { @@ -275,20 +269,20 @@ return @radio; } -# +# # Determine the order the choices should be in. -# +# sub orderedChoices { my $self = shift; my %choiceHash = $self->choiceHash; my @labels = keys %choiceHash; - - my @order = @{$self->{order}}; - my @first = @{$self->{first}}; - my @last = @{$self->{last}}; - + + my @order = @{$self->{order} || []}; + my @first = @{$self->{first} || []}; + my @last = @{$self->{last} || []}; + my @orderLabels; - + if (@order) { my %remainingChoices = %choiceHash; Value::Error("When using the 'order' option, you must list all possible choices.") @@ -306,7 +300,7 @@ my @firstLabels; my @lastLabels; my %remainingChoices = %choiceHash; - + foreach my $i (0..$#first) { my $label = $self->findChoice($first[$i]); Value::Error("Item $i of the 'first' option is not a choice.") @@ -316,7 +310,7 @@ push @firstLabels, $label; delete $remainingChoices{$label}; } - + foreach my $i (0..$#last) { my $label = $self->findChoice($last[$i]); Value::Error("Item $i of the 'last' option is not a choice.") @@ -334,8 +328,9 @@ # might we want to explicitly randomize these? @orderLabels = @labels; } - - return map { $_ => $choiceHash{$_} } @orderLabels; + + my $label = $self->findChoice($self->{checked}); + return map { ($_ eq $label ? "%$_" : $_) => $choiceHash{$_} } @orderLabels; } # @@ -366,4 +361,11 @@ sub ans_rule {shift->buttons(@_)} sub named_ans_rule {shift->named_buttons(@_)} +sub cmp_postprocess { + my $self = shift; my $ans = shift; + my $text = $self->labelText($ans->{student_value}->value); + $ans->{preview_text_string} = $ans->{student_ans} = $text; + $ans->{preview_latex_string} = "\\hbox{$text}"; +} + 1; Index: parserImplicitEquation.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/parserImplicitEquation.pl,v retrieving revision 1.2.2.2.2.1 retrieving revision 1.2.2.2.2.2 diff -Lmacros/parserImplicitEquation.pl -Lmacros/parserImplicitEquation.pl -u -r1.2.2.2.2.1 -r1.2.2.2.2.2 --- macros/parserImplicitEquation.pl +++ macros/parserImplicitEquation.pl @@ -214,12 +214,12 @@ my $self = shift; my $class = ref($self) || $self; my $context = (Value::isContext($_[0]) ? shift : $self->context); my $f = shift; return $f if ref($f) eq $class; - $f = main::Formula($f); + $f = $context->Package("Formula")->new($context,$f); Value::Error("Your formula doesn't look like an implicit equation") unless $f->type eq 'Equality'; my $F = ($context->Package("Formula")->new($context,$f->{tree}{lop}) - $context->Package("Formula")->new($context,$f->{tree}{rop}))->reduce; - $F = $context->Package("Formula")->new($F) unless Value::isFormula($F); + $F = $context->Package("Formula")->new($context,$F) unless Value::isFormula($F); Value::Error("Your equation must be real-valued") unless $F->isRealNumber; Value::Error("Your equation should not be constant") if $F->isConstant; Value::Error("Your equation can not contain adaptive parameters") Index: contextString.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/contextString.pl,v retrieving revision 1.4.2.2.2.1 retrieving revision 1.4.2.2.2.2 diff -Lmacros/contextString.pl -Lmacros/contextString.pl -u -r1.4.2.2.2.1 -r1.4.2.2.2.2 --- macros/contextString.pl +++ macros/contextString.pl @@ -80,6 +80,7 @@ sub Init { my $context = $main::context{String} = Parser::Context->getCopy("Numeric"); $context->{name} = "String"; + $context->{pattern}{number} = qr/^$/; $context->parens->clear(); $context->variables->clear(); $context->constants->clear(); Index: PGbasicmacros.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/PGbasicmacros.pl,v retrieving revision 1.47.2.2.2.6 retrieving revision 1.47.2.2.2.7 diff -Lmacros/PGbasicmacros.pl -Lmacros/PGbasicmacros.pl -u -r1.47.2.2.2.6 -r1.47.2.2.2.7 --- macros/PGbasicmacros.pl +++ macros/PGbasicmacros.pl @@ -998,22 +998,23 @@ my @in = @_; my $out = ''; my $permissionLevel = PG_restricted_eval(q!$main::envir{permissionLevel}!); - my $PRINT_FILE_NAMES_PERMISSION_LEVEL = PG_restricted_eval(q!$envir->{'PRINT_FILE_NAMES_PERMISSION_LEVEL'}!); + my $PRINT_FILE_NAMES_PERMISSION_LEVEL = (PG_restricted_eval(q!defined( $main::envir{'PRINT_FILE_NAMES_PERMISSION_LEVEL'} )!))? + PG_restricted_eval(q!$main::envir{'PRINT_FILE_NAMES_PERMISSION_LEVEL'}!) : 10000; # protect against undefined values my $printHintForInstructor = $permissionLevel >= $PRINT_FILE_NAMES_PERMISSION_LEVEL; my $showHint = PG_restricted_eval(q!$main::showHint!); - my $displayHint = PG_restricted_eval(q!$envir->{'displayHintsQ'}!); + my $displayHint = PG_restricted_eval(q!$main::envir{'displayHintsQ'}!); PG_restricted_eval(q!$main::hintExists =1!); PG_restricted_eval(q!$main::numOfAttempts = 0 unless defined($main::numOfAttempts);!); my $attempts = PG_restricted_eval(q!$main::numOfAttempts!); if ($displayMode eq 'TeX') { if ($printHintForInstructor) { - $out = join(' ',@in, "$BR(Show hint after $showHint attempts. ) $BR"); + $out = join(' ', "$BR(Show the student hint after $showHint attempts: ) $BR",@in); } else { $out = ''; # do nothing since hints are not available for download for students } } elsif ($printHintForInstructor) { # always print hints for instructor types - $out = join(' ',@in, "$BR(Show hint after $showHint attempts. )$BR "); + $out = join(' ', "$BR( Show the student hint after $showHint attempts. The current number of attempts is $attempts. )$BR $BBOLD HINT: $EBOLD ", @in); } elsif ( $displayHint and ( $attempts > $showHint )) { ## the second test above prevents a hint being shown if a doctored form is submitted Index: contextLimitedPolynomial.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/contextLimitedPolynomial.pl,v retrieving revision 1.5.4.2.2.1 retrieving revision 1.5.4.2.2.2 diff -Lmacros/contextLimitedPolynomial.pl -Lmacros/contextLimitedPolynomial.pl -u -r1.5.4.2.2.1 -r1.5.4.2.2.2 --- macros/contextLimitedPolynomial.pl +++ macros/contextLimitedPolynomial.pl @@ -234,7 +234,7 @@ sub checkStrict { my $self = shift; - $self->Error("You can only use '%s' between a coefficent and a variable in a polynomial",$self->{bop}); + $self->Error("You can only use '%s' between coefficents and variables in a polynomial",$self->{bop}); } ############################################## Index: AppletObjects.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/AppletObjects.pl,v retrieving revision 1.11.2.1.2.1 retrieving revision 1.11.2.1.2.2 diff -Lmacros/AppletObjects.pl -Lmacros/AppletObjects.pl -u -r1.11.2.1.2.1 -r1.11.2.1.2.2 --- macros/AppletObjects.pl +++ macros/AppletObjects.pl @@ -184,7 +184,7 @@ my $state_input_element = ($debugMode) ? $debug_input_element : qq!\n<input type="hidden" name = "$appletStateName" value ="$base_64_encoded_answer_value">!; my $reset_button_str = ($reset_button) ? - qq!<input type='button' value='return this question to its initial state' onClick="setAppletStateToRestart('$appletName')"><br/>! + qq!<input type='submit' name='previewAnswers' value='return this question to its initial state' onClick="setAppletStateToRestart('$appletName')"><br/>! : '' ; # <input type="button" value="reinitialize applet" onClick="getQE('$appletStateName').value='$base64_initialState'"/><br/> # always base64 encode the hidden answer value to prevent problems with quotes. Index: parserFormulaUpToConstant.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/parserFormulaUpToConstant.pl,v retrieving revision 1.11.2.1.2.3 retrieving revision 1.11.2.1.2.4 diff -Lmacros/parserFormulaUpToConstant.pl -Lmacros/parserFormulaUpToConstant.pl -u -r1.11.2.1.2.3 -r1.11.2.1.2.4 --- macros/parserFormulaUpToConstant.pl +++ macros/parserFormulaUpToConstant.pl @@ -173,12 +173,16 @@ # If constants aren't the same, substitute the professor's in the student answer. # $r = $r->substitute($r->{constant}=>$l->{constant}) unless $r->{constant} eq $l->{constant}; + # # Compare with adaptive parameters to see if $l + n0 C = $r for some n0. # my $adapt = $l->adapt; - my $equal = $adapt->cmp_compare($r,{}); + my $equal = Parser::Eval(sub {$adapt == $r}); $self->{adapt} = $self->{adapt}->inherit($adapt); # save the adapted value's flags + $self->{adapt}{test_values} = $adapt->{test_values}; # (these two are removed by inherit) + $self->{adapt}{test_adapt} = $adapt->{test_adapt}; + $_[1]->{test_values} = $r->{test_values}; # save these in student answer for diagnostics return -1 unless $equal; # # Check that n0 is non-zero (i.e., there is a multiple of C in the student answer) @@ -192,7 +196,6 @@ # sub adapt { my $self = shift; - my $adapt = $self->{adapt}->inherit($self); delete $adapt->{adapt}; return $self->adjustInherit($self->{adapt}); } @@ -262,7 +265,10 @@ # sub cmp_diagnostics { my $self = shift; - $self->inherit($self->{adapt})->SUPER::cmp_diagnostics(@_); + my $adapt = $self->inherit($self->{adapt}); + $adapt->{test_values} = $self->{adapt}{test_values}; # these aren't copied by inherit + $adapt->{test_adapt} = $self->{adapt}{test_adapt}; + $adapt->SUPER::cmp_diagnostics(@_); } # @@ -289,9 +295,7 @@ return unless $ans->{score} == 0 && !$ans->{isPreview}; return if $ans->{ans_message} || !$self->getFlag("showHints"); my $student = $ans->{student_value}; - $main::{_cmp_} = sub {return $ans->{correct_value} <=> $student}; # compare encodes the reason in the result - my $result = main::PG_restricted_eval('&{$main::{_cmp_}}'); - delete $main::{_cmp_}; + my $result = Parser::Eval(sub {return $ans->{correct_value} <=> $student}); # compare encodes the reason in the result $self->cmp_Error($ans,"Note: there is always more than one posibility") if $result == 2 || $result == 3; if ($result == 3) { my $context = $self->context; Index: parserPopUp.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/parserPopUp.pl,v retrieving revision 1.1.6.2.2.2 retrieving revision 1.1.6.2.2.3 diff -Lmacros/parserPopUp.pl -Lmacros/parserPopUp.pl -u -r1.1.6.2.2.2 -r1.1.6.2.2.3 --- macros/parserPopUp.pl +++ macros/parserPopUp.pl @@ -76,6 +76,7 @@ # sub new { my $self = shift; my $class = ref($self) || $self; + shift if Value::isContext($_[0]); # remove context, if given (it is not used) my $choices = shift; my $value = shift; Value::Error("A PopUp's first argument should be a list of menu items") unless ref($choices) eq 'ARRAY'; Index: contextPiecewiseFunction.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/contextPiecewiseFunction.pl,v retrieving revision 1.9.2.1 retrieving revision 1.9.2.1.2.1 diff -Lmacros/contextPiecewiseFunction.pl -Lmacros/contextPiecewiseFunction.pl -u -r1.9.2.1 -r1.9.2.1.2.1 --- macros/contextPiecewiseFunction.pl +++ macros/contextPiecewiseFunction.pl @@ -64,12 +64,25 @@ Context()->texStrings; BEGIN_TEXT - Suppose \(f(x)=$f\). Then \(f($a)\) = \{ans_rule(20)\}. + If \[f(x)=$f\] then \(f($a)\) = \{ans_rule(20)\}. END_TEXT Context()->normalStrings; ANS($f->eval(x=>$a)->cmp); +Normally when you use a piecewise function at the end of a sentence, +the period is placed at the end of the last case. Since + + \[ f(x) = $f \]. + +would put the period centered at the right-hand side of the function, +this is not what is desired. To get a period at the end of the last +case, use + + \[ f(x) = \{$f->with(final_period=>1)\} \] + +instead. + =cut loadMacros("MathObjects.pl"); @@ -699,7 +712,7 @@ # sub compareInterval { my $self = shift; my ($D,$f0,$f1) = @_; - my ($a,$b) = $D->value; $a = $a->value; $b = $b=>value; + my ($a,$b) = $D->value; $a = $a->value; $b = $b->value; return $f0 == $f1 if $D->{leftInfinite} && $D->{rightInfinite}; $a = $b - 2 if $D->{leftInfinite}; $b = $a + 2 if $D->{rightInfinite}; @@ -741,12 +754,13 @@ # sub string { my $self = shift; my @cases = (); + my $period = ($self->{final_period} ? "." : ""); foreach my $If (@{$self->{data}}) { my ($I,$f) = @{$If}; push(@cases,$f->string." if ".$I->string); } push(@cases,$self->{otherwise}->string) if defined $self->{otherwise}; - join(" else\n",@cases); + join(" else\n",@cases) . $period; } # @@ -754,13 +768,14 @@ # sub TeX { my $self = shift; my @cases = (); + my $period = ($self->{final_period} ? "." : ""); foreach my $If (@{$self->{data}}) { my ($I,$f) = @{$If}; push(@cases,'\displaystyle{'.$f->TeX."}&\\text{if}\\ ".$I->TeX); } if (scalar(@cases)) { push(@cases,'\displaystyle{'.$self->{otherwise}->TeX.'}&\text{otherwise}') if defined $self->{otherwise}; - return '\begin{cases}'.join('\cr'."\n",@cases).'\end{cases}'; + return '\begin{cases}'.join('\cr'."\n",@cases).$period.'\end{cases}'; } else { return $self->{otherwise}->TeX; } Index: dangerousMacros.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/dangerousMacros.pl,v retrieving revision 1.41.2.3.2.1 retrieving revision 1.41.2.3.2.2 diff -Lmacros/dangerousMacros.pl -Lmacros/dangerousMacros.pl -u -r1.41.2.3.2.1 -r1.41.2.3.2.2 --- macros/dangerousMacros.pl +++ macros/dangerousMacros.pl @@ -1076,6 +1076,7 @@ # .gif FILES in TeX mode ################################################################################ + $setNumber =~ s/\./_/g; ## extra dots confuse latex's graphics package if ($envir{texDisposition} eq "pdf") { # We're going to create PDF files with our TeX (using pdflatex), so we # need images in PNG format. @@ -1208,6 +1209,7 @@ # .png FILES in TeX mode ################################################################################ + $setNumber =~ s/\./_/g; ## extra dots confuse latex's graphics package if ($envir{texDisposition} eq "pdf") { # We're going to create PDF files with our TeX (using pdflatex), so we # need images in PNG format. what luck! they're already in PDF format! Index: contextInequalities.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/contextInequalities.pl,v retrieving revision 1.18.2.1 retrieving revision 1.18.2.1.2.1 diff -Lmacros/contextInequalities.pl -Lmacros/contextInequalities.pl -u -r1.18.2.1 -r1.18.2.1.2.1 --- macros/contextInequalities.pl +++ macros/contextInequalities.pl @@ -114,11 +114,11 @@ '+' => {class => "Inequalities::BOP::add"}, '-' => {class => "Inequalities::BOP::subtract"}, ); - $context->parens->set("(" => {type => "List", formIntervla => ']'}); # trap these later - $context->parens->set("[" => {type => "List", formIntervla => ')'}); # trap these later + $context->parens->set("(" => {type => "List", formInterval => ']'}); # trap these later + $context->parens->set("[" => {type => "List", formInterval => ')'}); # trap these later $context->strings->remove("NONE"); $context->constants->add(NONE=>Value::Set->new()); - $context->flags->set(noneWord => 'NONE'); + $context->flags->set(noneWord => 'NONE', showNotEquals => 1); $context->{parser}{Variable} = "Inequalities::Variable"; $context->{value}{'Interval()'} = "Inequalities::MakeInterval"; $context->{value}{Inequality} = "Inequalities::Inequality"; @@ -218,8 +218,8 @@ sub evalGreaterThan { my ($self,$a,$b) = @_; my $context = $self->context; my $I = Value::Infinity->new; - return $self->Package("Interval")->new($context,'(',$b,$I,')') if $self->{varPos} eq 'lop'; - return $self->Package("Interval")->new($context,'(',-$I,$a,')'); + return $self->Package("Interval")->new($context,'(',$b,$I,')')->with(reversed=>1) if $self->{varPos} eq 'lop'; + return $self->Package("Interval")->new($context,'(',-$I,$a,')')->with(reversed=>1); } sub evalLessThanOrEqualTo { @@ -232,8 +232,8 @@ sub evalGreaterThanOrEqualTo { my ($self,$a,$b) = @_; my $context = $self->context; my $I = Value::Infinity->new; - return $self->Package("Interval")->new($context,'[',$b,$I,')') if $self->{varPos} eq 'lop'; - return $self->Package("Interval")->new($context,'(',-$I,$a,']'); + return $self->Package("Interval")->new($context,'[',$b,$I,')')->with(reversed=>1) if $self->{varPos} eq 'lop'; + return $self->Package("Interval")->new($context,'(',-$I,$a,']')->with(reversed=>1); } sub evalEqualTo { @@ -249,7 +249,7 @@ return $self->Package("Union")->new($context, $self->Package("Interval")->new($context,'(',-$I,$x,')'), $self->Package("Interval")->new($context,'(',$x,$I,')') - ); + )->with(notEqual=>1); } # @@ -534,19 +534,28 @@ sub type {"Interval"} +sub updateParts { + my $self = shift; + $self->{leftInfinite} = 1 if $self->{data}[0]->{isInfinite}; + $self->{rightInfinite} = 1 if $self->{data}[1]->{isInfinite}; +} + sub string { my $self = shift; my ($a,$b,$open,$close) = $self->value; my $x = $self->{varName} || ($self->context->variables->names)[0]; $x = $context->{variables}{$x}{string} if defined $context->{variables}{$x}{string}; - my $left = ($open eq '(' ? ' < ' : ' <= '); - my $right = ($close eq ')' ? ' < ' : ' <= '); - my $inequality = ""; - $inequality .= $a->string.$left unless $self->{leftInfinite}; - $inequality .= $x; - $inequality .= $right.$b->string unless $self->{rightInfinite}; - $inequality = "-infinity < $x < infinity" if $inequality eq $x; - return $inequality; + if ($self->{leftInfinite}) { + return "-infinity < $x < infinity" if $self->{rightInfinite}; + return $b->string . ($close eq ')' ? ' > ' : ' >= ') . $x if $self->{reversed}; + return $x . ($close eq ')' ? ' < ' : ' <= ') . $b->string; + } elsif ($self->{rightInfinite}) { + return $x . ($open eq '(' ? ' > ' : ' >= ') . $a->string if $self->{reversed}; + return $a->string . ($open eq '(' ? ' < ' : ' <= ') . $x; + } else { + return $a->string . ($open eq '(' ? ' < ' : ' <= ') . + $x . ($close eq ')' ? ' < ' : ' <= ') . $b->string; + } } sub TeX { @@ -556,14 +565,17 @@ my $x = $self->{varName} || ($context->variables->names)[0]; $x = $context->{variables}{$x}{TeX} if defined $context->{variables}{$x}{TeX}; $x =~ s/^([^_]+)_?(\d+)$/$1_{$2}/; - my $left = ($open eq '(' ? ' < ' : ' \le '); - my $right = ($close eq ')' ? ' < ' : ' \le '); - my $inequality = ""; - $inequality .= $a->string.$left unless $self->{leftInfinite}; - $inequality .= $x; - $inequality .= $right.$b->string unless $self->{rightInfinite}; - $inequality = "-\\infty < $x < \\infty " if $inequality eq $x; - return $inequality; + if ($self->{leftInfinite}) { + return "-\\infty < $x < \\infty" if $self->{rightInfinite}; + return $b->TeX . ($close eq ')' ? ' > ' : ' \ge ') . $x if $self->{reversed}; + return $x . ($close eq ')' ? ' < ' : ' \le ') . $b->TeX; + } elsif ($self->{rightInfinite}) { + return $x . ($open eq '(' ? ' > ' : ' \ge ') . $a->TeX if $self->{reversed}; + return $a->TeX . ($open eq '(' ? ' < ' : ' \le ') . $x; + } else { + return $a->TeX . ($open eq '(' ? ' < ' : ' \le ') . + $x . ($close eq ')' ? ' < ' : ' \le ') . $b->TeX; + } } ################################################## @@ -607,26 +619,60 @@ sub string { my $self = shift; my $equation = shift; shift; shift; my $prec = shift; - my $op = ($equation->{context} || $self->context)->{operators}{'or'}; - my @intervals = (); + return $self->display("string",$equation,$prec); +} + +sub TeX { + my $self = shift; + my $equation = shift; shift; shift; my $prec = shift; + return $self->display("TeX",$equation,$prec); +} + +sub display { + my $self = shift; my $method = shift; my $equation = shift; my $prec = shift; + my $context = ($equation->{context} || $self->context); + my $X = $self->{varName} || ($context->variables->names)[0]; + $X = $context->{variables}{$X}{$method} if defined $context->{variables}{$X}{$method}; + $X =~ s/^([^_]+)_?(\d+)$/$1_{$2}/ if $method eq 'TeX'; + my $op = $context->{operators}{'or'}; + my ($and,$or,$le,$ge,$ne,$open,$close) = @{{ + string => [' and ',$op->{string} || ' or ',' <= ',' >= ',' != ','(',')'], + TeX => ['\hbox{ and }',$op->{TeX} || $op->{string} || '\hbox{ or }', + ' \le ',' \ge ',' \ne ','\left(','\right)'], + }->{$method}}; + my $showNE = $self->getFlag("showNotEquals",1); + my @intervals = (); my @points = (); my $interval; foreach my $x (@{$self->data}) { $x->{format} = $self->{format} if defined $self->{format}; - push(@intervals,$x->string($equation)) + if ($x->type eq 'Interval' && $showNE) { + if (defined($interval)) { + if ($interval->{data}[1] == $x->{data}[0]) { + push(@points,$X.$ne.$x->{data}[0]->$method($equation)); + $interval = $interval->with(isCopy=>1, data=>[$interval->value]) unless $interval->{isCopy}; + $interval->{data}[1] = $x->{data}[1]; + $interval->{rightInfinite} = 1 if $x->{rightInfinite}; + next; + } + push(@intervals,$self->joinAnd($interval,$method,$and,$equation,@points)); + } + $interval = $x; @points = (); next; + } + if (defined($interval)) { + push(@intervals,$self->joinAnd($interval,$method,$and,$equation,@points)); + $interval = undef; @points = (); + } + push(@intervals,$x->$method($equation)); } - my $string = join($op->{string} || ' or ',@intervals); - $string = '('.$string.')' if defined($prec) && $prec > ($op->{precedence} || 1.5); + push(@intervals,$self->joinAnd($interval,$method,$and,$equation,@points)) if defined($interval); + my $string = join($or,@intervals); + $string = $open.$string.$close if defined($prec) && $prec > ($op->{precedence} || 1.5); return $string; } -sub TeX { - my $self = shift; - my $equation = shift; shift; shift; my $prec = shift; - my $op = ($equation->{context} || $self->context)->{operators}{'or'}; - my @intervals = (); - foreach my $x (@{$self->data}) {push(@intervals,$x->TeX($equation))} - my $TeX = join($op->{TeX} || $op->{string} || '\hbox{ or }',@intervals); - $TeX = '\left('.$TeX.'\right)' if defined($prec) && $prec > ($op->{precedence} || 1.5); - return $TeX; +sub joinAnd { + my $self = shift; $interval = shift; $method = shift, my $and = shift; my $equation = shift; + unshift(@_,$interval->$method($equation)) unless $interval->{leftInfinite} && $interval->{rightInfinite}; + return join($and, @_); } ################################################## |