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: Sam H. v. a. <we...@ma...> - 2005-08-11 22:10:38
|
Log Message: ----------- added implementation of options() which call optionsMacro(). the version in Problem.pm takes care to preserve the editMode and sourceFilePath parameters. Modified Files: -------------- webwork2/htdocs/css: ur.css webwork2/lib/WeBWorK: ContentGenerator.pm webwork2/lib/WeBWorK/ContentGenerator: Problem.pm ProblemSet.pm Revision Data ------------- Index: ur.css =================================================================== RCS file: /webwork/cvs/system/webwork2/htdocs/css/ur.css,v retrieving revision 1.4 retrieving revision 1.5 diff -Lhtdocs/css/ur.css -Lhtdocs/css/ur.css -u -r1.4 -r1.5 --- htdocs/css/ur.css +++ htdocs/css/ur.css @@ -47,6 +47,7 @@ div.Message { font-style: italic; } div.Body { } div.Warnings { } +div.viewOptions { border: thin groove; padding: 1ex; margin: 2ex align: left; } /* background colors for success and failure messages */ div.ResultsWithoutError { background-color: #66ff99 } /* light green */ Index: ContentGenerator.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator.pm,v retrieving revision 1.143 retrieving revision 1.144 diff -Llib/WeBWorK/ContentGenerator.pm -Llib/WeBWorK/ContentGenerator.pm -u -r1.143 -r1.144 --- lib/WeBWorK/ContentGenerator.pm +++ lib/WeBWorK/ContentGenerator.pm @@ -737,105 +737,14 @@ =item options() -Default is defined in this package. +Not defined in this package. -Print an auxiliary options form, related to the content displayed in the -C<body>. +View options related to the content displayed in the body or info areas. See also +optionsMacro(). =cut -sub options { - my ($self) = @_; - - return "" if $self->{invalidProblem}; - my $sourceFilePathfield = ''; - if($self->r->param("sourceFilePath")) { - $sourceFilePathfield = CGI::hidden(-name => "sourceFilePath", - -value => $self->r->param("sourceFilePath")); - } - - my $r = $self->{r}; - my $ce = $self->{ce}; - # insure that certain defaults are defined - $self->{must} = {} unless defined $self->{must}; - $self->{can} = {} unless defined $self->{can}; - $self->{will} = {} unless defined $self->{will}; - - # displayMode - my $displayMode = $r->param("displayMode") || $ce->{pg}->{options}->{displayMode}; - $self->{displayMode} = $displayMode unless defined $self->{displayMode}; - - # showOldAnswers - my $want_to_showOldAnswers = defined($r->param("showOldAnswers")) ? - $r->param("showOldAnswers") : $ce->{pg}->{options}->{showOldAnswers}; - $self->{can}->{showOldAnswers} = 1; - $self->{will}->{showOldAnswers} = $self->{can}->{showOldAnswers} && $want_to_showOldAnswers; - - return join("", - CGI::start_form("POST", $self->{r}->uri), - $self->hidden_authen_fields, - $sourceFilePathfield, - CGI::hr(), - CGI::start_div({class=>"viewOptions"}), - $self->viewOptions(), - CGI::end_div(), - CGI::end_form() - ); -} - -sub viewOptions { - my ($self) = @_; - my $ce = $self->r->ce; - - # don't show options if we don't have anything to show - return if $self->{invalidSet} or $self->{invalidProblem}; - #return unless $self->{isOpen}; - - my $displayMode = $self->{displayMode}; - my %must = %{ $self->{must} }; - my %can = %{ $self->{can} }; - my %will = %{ $self->{will} }; - - my $optionLine; - $can{showOldAnswers} and $optionLine .= join "", - "Show saved answers?".CGI::br(), - CGI::radio_group( - -name => "showOldAnswers", - -values => [1,0], - -default => $will{showOldAnswers}, - -labels => { - 0 => 'No', - 1 => 'Yes', - }, - ), .CGI::br(); - - $optionLine and $optionLine .= join "", CGI::br(); - - my %display_modes = %{WeBWorK::PG::DISPLAY_MODES()}; - my @active_modes = grep { exists $display_modes{$_} } - @{$ce->{pg}->{displayModes}}; - my $modeLine = (scalar(@active_modes) > 1) ? - "View equations as: ".CGI::br(). - CGI::radio_group( - -name => "displayMode", - -values => \@active_modes, - -default => $displayMode, - -linebreak=>'true', - -labels => { - plainText => "plain", - formattedText => "formatted", - images => "images", - jsMath => "jsMath", - asciimath => "asciimath", - }, - ). CGI::br().CGI::hr() : ''; - - return CGI::div({-style=>"border: thin groove; padding: 1ex; margin: 2ex align: left"}, - $modeLine, - $optionLine, - CGI::submit(-name=>"redisplay", -label=>"Apply Options"), - ); -} +#sub options { } =item path($args) @@ -1375,6 +1284,75 @@ CGI::img({src=>$imageURL})); } +=item optionsMacro(options_to_show => \@options_to_show, extra_params => \@extra_params) + +Helper macro for displaying the View Options panel. + +@options_to_show lists the options to show, from among this list "displayMode", +"showOldAnswers", "showHints", "showSolutions". If no options are given, +"displayMode" is assumed. + +@extraParams is dereferenced and passed to the hidden_fields() method. Use this +to preserve state from the content generator calling optionsMacro(). + +This macro is intended to be called from an implementation of the options() +method. The simplest way to to this is: + + sub options { shift->optionsMacro } + +=cut + +sub optionsMacro { + my ($self, %options) = @_; + + my @options_to_show = @{$options{options_to_show}} if exists $options{options_to_show}; + @options_to_show = "displayMode" unless @options_to_show; + my %options_to_show; @options_to_show{@options_to_show} = (); # make hash for easy lookups + my @extra_params = @{$options{extra_params}} if exists $options{extra_params}; + + my $result = CGI::start_form("POST", $self->r->uri); + $result .= $self->hidden_authen_fields; + $result .= $self->hidden_fields(@extra_params) if @extra_params; + $result .= CGI::start_div({class=>"viewOptions"}); + + if (exists $options_to_show{displayMode}) { + my $curr_displayMode = $self->r->param("displayMode") || $self->r->ce->{pg}->{options}->{displayMode}; + my %display_modes = %{WeBWorK::PG::DISPLAY_MODES()}; + my @active_modes = grep { exists $display_modes{$_} } @{$self->r->ce->{pg}->{displayModes}}; + if (@active_modes > 1) { + $result .= "View equations as: "; + $result .= CGI::br(); + $result .= CGI::radio_group( + -name => "displayMode", + -values => \@active_modes, + -default => $curr_displayMode, + -linebreak=>'true', + ); + $result .= CGI::br(); + } + } + + if (exists $options_to_show{showOldAnswers}) { + my $curr_showOldAnswers = $self->r->param("showOldAnswers"); + $result .= "Show saved answers?"; + $result .= CGI::br(); + $result .= CGI::radio_group( + -name => "showOldAnswers", + -values => [1,0], + -default => $curr_showOldAnswers, + -labels => { 0=>'No', 1=>'Yes' }, + ); + $result .= CGI::br(); + } + + $result .= CGI::br(); + $result .= CGI::submit(-name=>"redisplay", -label=>"Apply Options"); + $result .= CGI::end_div(); + $result .= CGI::end_form(); + + return $result; +} + =back =cut Index: Problem.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/Problem.pm,v retrieving revision 1.177 retrieving revision 1.178 diff -Llib/WeBWorK/ContentGenerator/Problem.pm -Llib/WeBWorK/ContentGenerator/Problem.pm -u -r1.177 -r1.178 --- lib/WeBWorK/ContentGenerator/Problem.pm +++ lib/WeBWorK/ContentGenerator/Problem.pm @@ -644,27 +644,27 @@ return $self->{pg}->{head_text} if $self->{pg}->{head_text}; } -# sub options { -# my ($self) = @_; -# warn "doing options in Problem"; -# return "" if $self->{invalidProblem}; -# my $sourceFilePathfield = ''; -# if($self->r->param("sourceFilePath")) { -# $sourceFilePathfield = CGI::hidden(-name => "sourceFilePath", -# -value => $self->r->param("sourceFilePath")); -# } -# -# return join("", -# CGI::start_form("POST", $self->{r}->uri), -# $self->hidden_authen_fields, -# $sourceFilePathfield, -# CGI::hr(), -# CGI::start_div({class=>"viewOptions"}), -# $self->viewOptions(), -# CGI::end_div(), -# CGI::end_form() -# ); -# } +sub options { + my ($self) = @_; + #warn "doing options in Problem"; + + # don't show options if we don't have anything to show + return if $self->{invalidSet} or $self->{invalidProblem}; + return unless $self->{isOpen}; + + my $displayMode = $self->{displayMode}; + my %can = %{ $self->{can} }; + + my @options_to_show = "displayMode"; + push @options_to_show, "showOldAnswers" if $can{showOldAnswers}; + push @options_to_show, "showHints" if $can{showHints}; + push @options_to_show, "showSolutions" if $can{showSolutions}; + + return $self->optionsMacro( + options_to_show => \@options_to_show, + extra_params => ["editMode", "sourceFilePath"], + ); +} sub siblings { my ($self) = @_; Index: ProblemSet.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/ProblemSet.pm,v retrieving revision 1.64 retrieving revision 1.65 diff -Llib/WeBWorK/ContentGenerator/ProblemSet.pm -Llib/WeBWorK/ContentGenerator/ProblemSet.pm -u -r1.64 -r1.65 --- lib/WeBWorK/ContentGenerator/ProblemSet.pm +++ lib/WeBWorK/ContentGenerator/ProblemSet.pm @@ -105,11 +105,18 @@ my $problemSetsPage = $urlpath->parent; my @links = ("Homework Sets" , $r->location . $problemSetsPage->path, "navUp"); - my $tail = "&displayMode=".$self->{displayMode}."&showOldAnswers=".$self->{will}->{showOldAnswers}; + # CRAP ALERT: this line relies on the hacky options() implementation in ContentGenerator. + # we need to find a better way to do this -- long range dependencies like this are dangerous! + #my $tail = "&displayMode=".$self->{displayMode}."&showOldAnswers=".$self->{will}->{showOldAnswers}; + # here is a hack to get some functionality back, but I don't even think it's that important to + # have this, since there are SO MANY PLACES where we lose the displayMode, etc. + # (oh boy, do we need a session table in the database!) + my $displayMode = $r->param("displayMode") || ""; + my $showOldAnswers = $r->param("showOldAnswers") || ""; + my $tail = "&displayMode=$displayMode&showOldAnswers=$showOldAnswers"; return $self->navMacro($args, $tail, @links); } - sub siblings { my ($self) = @_; my $r = $self->r; @@ -195,7 +202,7 @@ my $psvn = $set->psvn(); my $screenSetHeader = $set->set_header || $ce->{webworkFiles}->{screenSnippets}->{setHeader}; - my $displayMode = $ce->{pg}->{options}->{displayMode}; + my $displayMode = $r->param("displayMode") || $ce->{pg}->{options}->{displayMode}; if (defined $r->param("editMode") and $r->param("editMode") eq "temporaryFile") { $screenSetHeader = "$screenSetHeader.$userID.tmp"; @@ -250,6 +257,8 @@ return ""; } +sub options { shift->optionsMacro } + sub body { my ($self) = @_; my $r = $self->r; |
From: Sam H. v. a. <we...@ma...> - 2005-08-11 21:39:14
|
Log Message: ----------- revert to using mydisplayMode for the param name (whoops). Modified Files: -------------- webwork2/lib/WeBWorK/ContentGenerator/Instructor: SetMaker.pm Revision Data ------------- Index: SetMaker.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm,v retrieving revision 1.50 retrieving revision 1.51 diff -Llib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm -Llib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm -u -r1.50 -r1.51 --- lib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm +++ lib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm @@ -294,10 +294,10 @@ push @active_modes, 'None'; # We have our own displayMode since its value may be None, which is illegal # in other modules. - my $displayMode = $r->param('displayMode') || $r->ce->{pg}->{options}->{displayMode}; - $result .= ' Display Mode: '.CGI::popup_menu(-name=> 'displayMode', + my $mydisplayMode = $r->param('mydisplayMode') || $r->ce->{pg}->{options}->{displayMode}; + $result .= ' Display Mode: '.CGI::popup_menu(-name=> 'mydisplayMode', -values=>\@active_modes, - -default=> $displayMode); + -default=> $mydisplayMode); # Now we give a choice of the number of problems to show my $defaultMax = $r->param('max_shown') || MAX_SHOW_DEFAULT; $result .= ' Max. Shown: '. @@ -821,7 +821,7 @@ params=>{sourceFilePath => "$sourceFileName", problemSeed=> $problem_seed} )}, "Edit it" ); - my $displayMode = $self->r->param("displayMode"); + my $displayMode = $self->r->param("mydisplayMode"); $displayMode = $self->r->ce->{pg}->{options}->{displayMode} if not defined $displayMode or $displayMode eq "None"; my $try_link = CGI::a({href=>$self->systemLink( @@ -1252,7 +1252,7 @@ renderProblems(r=> $r, user => $user, problem_list => [@pg_files[$first_shown..$last_shown]], - displayMode => $r->param('displayMode')) : (); + displayMode => $r->param('mydisplayMode')) : (); ########## Top part print CGI::startform({-method=>"POST", -action=>$r->uri, -name=>'mainform'}), |
From: dpvc v. a. <we...@ma...> - 2005-08-11 20:55:35
|
Log Message: ----------- Added ability to subtract intervals, sets and unions. Adjusted the precedence of the union 'U' to be above _ and + so that things like (1,5) U (7,10) - {8} U (2,3) will do ((1,5) U (7,10)) - ({8} U (2,3)) rather than the previous (1,5) U ((7,10) - {8}) U (2,3). Finally, added a constant 'R' to the Interval context that is equivalent to (-inf,inf), so you can do things like R-{0} now. Still need to work out reducing unions so that things like (1,3)U(2,4) can become (1,4). Modified Files: -------------- pg/lib/Parser: Constant.pm pg/lib/Parser/BOP: subtract.pm union.pm pg/lib/Parser/Context: Default.pm pg/lib/Value: Interval.pm Set.pm Union.pm Revision Data ------------- Index: Constant.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser/Constant.pm,v retrieving revision 1.7 retrieving revision 1.8 diff -Llib/Parser/Constant.pm -Llib/Parser/Constant.pm -u -r1.7 -r1.8 --- lib/Parser/Constant.pm +++ lib/Parser/Constant.pm @@ -24,6 +24,8 @@ ref => $ref, equation => $equation }, $class; $c->{isConstant} = 1 if $const->{isConstant}; + $c->{canBeInterval} = 1 + if Value::isValue($const->{value}) && $const->{value}{canBeInterval}; return $c; } Index: union.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser/BOP/union.pm,v retrieving revision 1.7 retrieving revision 1.8 diff -Llib/Parser/BOP/union.pm -Llib/Parser/BOP/union.pm -u -r1.7 -r1.8 --- lib/Parser/BOP/union.pm +++ lib/Parser/BOP/union.pm @@ -14,7 +14,7 @@ sub _check { my $self = shift; return if ($self->checkStrings()); - if ($self->{lop}->{canBeInterval} && $self->{rop}->{canBeInterval}) { + if ($self->{lop}{canBeInterval} && $self->{rop}{canBeInterval}) { $self->{type} = Value::Type('Union',2,$Value::Type{number}); $self->{canBeInterval} = 1; foreach my $op ('lop','rop') { Index: subtract.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser/BOP/subtract.pm,v retrieving revision 1.5 retrieving revision 1.6 diff -Llib/Parser/BOP/subtract.pm -Llib/Parser/BOP/subtract.pm -u -r1.5 -r1.6 --- lib/Parser/BOP/subtract.pm +++ lib/Parser/BOP/subtract.pm @@ -14,6 +14,24 @@ return if ($self->checkStrings()); return if ($self->checkLists()); return if ($self->checkNumbers()); + if ($self->{lop}{canBeInterval} && $self->{rop}{canBeInterval}) { + if ($self->{lop}->type =~ m/Interval|Union|Set/ || + $self->{rop}->type =~ m/Interval|Union|Set/) { + $self->{type} = Value::Type('Union',2,$Value::Type{number}); + $self->{canBeInterval} = 1; + foreach my $op ('lop','rop') { + if ($self->{$op}->type !~ m/Interval|Union|Set/) { + if ($self->{$op}->class eq 'Value') { + $self->{$op}{value} = Value::Interval::promote($self->{$op}{value}); + } else { + $self->{$op} = bless $self->{$op}, 'Parser::List::Interval'; + } + $self->{$op}->typeRef->{name} = $self->{equation}{context}{parens}{interval}{type}; + } + } + } + return; + } my ($ltype,$rtype) = $self->promotePoints(); if (Parser::Item::typeMatch($ltype,$rtype)) {$self->{type} = $ltype} else {$self->matchError($ltype,$rtype)} Index: Default.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser/Context/Default.pm,v retrieving revision 1.28 retrieving revision 1.29 diff -Llib/Parser/Context/Default.pm -Llib/Parser/Context/Default.pm -u -r1.28 -r1.29 --- lib/Parser/Context/Default.pm +++ lib/Parser/Context/Default.pm @@ -11,15 +11,15 @@ ',' => {precedence => 0, associativity => 'left', type => 'bin', string => ',', class => 'Parser::BOP::comma', isComma => 1}, - 'U' => {precedence => 0.5, associativity => 'left', type => 'bin', isUnion => 1, - string => ' U ', TeX => '\cup ', class => 'Parser::BOP::union'}, - '+' => {precedence => 1, associativity => 'left', type => 'both', string => '+', class => 'Parser::BOP::add'}, '-' => {precedence => 1, associativity => 'left', type => 'both', string => '-', perl => '- ', class => 'Parser::BOP::subtract', rightparens => 'same'}, + 'U' => {precedence => 1.5, associativity => 'left', type => 'bin', isUnion => 1, + string => ' U ', TeX => '\cup ', class => 'Parser::BOP::union'}, + '><'=> {precedence => 2, associativity => 'left', type => 'bin', string => ' >< ', TeX => '\times ', perl => ' x ', fullparens => 1, class => 'Parser::BOP::cross'}, @@ -295,6 +295,10 @@ '[' => {type => 'Interval'}, '{' => {type => 'Set', removable => 0, emptyOK => 1}, ); +my $infinity = Value::Infinity->new(); +$intervalContext->constants->add( + R => Value::Interval->new('(',-$infinity,$infinity,')'), +); ######################################################################### Index: Set.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Set.pm,v retrieving revision 1.1 retrieving revision 1.2 diff -Llib/Value/Set.pm -Llib/Value/Set.pm -u -r1.1 -r1.2 --- lib/Value/Set.pm +++ lib/Value/Set.pm @@ -9,6 +9,7 @@ use overload '+' => sub {shift->add(@_)}, + '-' => sub {shift->sub(@_)}, '.' => \&Value::_dot, 'x' => sub {shift->cross(@_)}, '<=>' => sub {shift->compare(@_)}, @@ -86,11 +87,7 @@ sub add { my ($l,$r,$flag) = @_; if ($l->promotePrecedence($r)) {return $r->add($l,!$flag)} - $r = promote($r); - if ($flag) {my $tmp = $l; $l = $r; $r = $tmp} - Value::Error("Sets can only be added to Intervals, Sets or Unions") - unless Value::class($l) =~ m/Interval|Union|Set/ && - Value::class($r) =~ m/Interval|Union|Set/; + $r = promote($r); if ($flag) {my $tmp = $l; $l = $r; $r = $tmp} return Value::Union->new($l,$r) unless Value::class($l) eq 'Set' && Value::class($r) eq 'Set'; my @combined = (sort {$a <=> $b} (@{$l->data},@{$r->data})); @@ -103,6 +100,79 @@ } sub dot {my $self = shift; $self->add(@_)} +# +# Subtraction removes items from a set +# +sub sub { + my ($l,$r,$flag) = @_; + if ($l->promotePrecedence($r)) {return $r->sub($l,!$flag)} + $r = promote($r); if ($flag) {my $tmp = $l; $l = $r; $r = $tmp} + return Value::Union::form(subIntervalSet($l,$r)) if Value::class($l) eq 'Interval'; + return Value::Union::form(subSetInterval($l,$r)) if Value::class($r) eq 'Interval'; + return Value::Union::form(subSetSet($l,$r)); +} + +# +# Subtract one set from another +# (return the resulting set or nothing for empty set) +# +sub subSetSet { + my @l = sort {$a <=> $b} (@{$_[0]->data}); + my @r = sort {$a <=> $b} (@{$_[1]->data}); + my @entries = (); + while (scalar(@l) && scalar(@r)) { + if ($l[0] < $r[0]) {push(@entries,shift(@l))} + else {if ($l[0] == $r[0]) {shift(@l)}; shift(@r)} + } + push(@entries,@l); + return () unless scalar(@entries); + return $pkg->make(@entries); +} + +# +# Subtract a set from an interval +# (returns a collection of intervals) +# +sub subIntervalSet { + my $I = shift; my $S = shift; + my @union = (); my ($a,$b) = $I->value; + foreach my $x ($S->value) { + next if $x < $a; + if ($x == $a) { + return @union if $a == $b; + $I->{open} = '('; + } elsif ($x < $b) { + push(@union,Value::Interval->new($I->{open},$a,$x,')')); + $I->{open} = '('; $I->{data}[0] = $x; + } else { + $I->{close} = ')' if ($x == $b); + last; + } + } + return (@union,$I); +} + +# +# Subtract an interval from a set +# (returns the resulting set or nothing for the empty set) +# +sub subSetInterval { + my $S = shift; my $I = shift; + my ($a,$b) = $I->value; + my @entries = (); + foreach my $x ($S->value) { + push(@entries,$x) + if ($x < $a || $x > $b) || + ($x == $a && $I->{open} ne '[') || + ($x == $b && $I->{close} ne ']'); + } + return () unless scalar(@entries); + return $pkg->make(@entries); +} + +# +# Compare two sets lexicographically on their sorted contents +# sub compare { my ($l,$r,$flag) = @_; if ($l->promotePrecedence($r)) {return $r->compare($l,!$flag)} @@ -126,4 +196,3 @@ ########################################################################### 1; - Index: Interval.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Interval.pm,v retrieving revision 1.21 retrieving revision 1.22 diff -Llib/Value/Interval.pm -Llib/Value/Interval.pm -u -r1.21 -r1.22 --- lib/Value/Interval.pm +++ lib/Value/Interval.pm @@ -11,6 +11,7 @@ use overload '+' => sub {shift->add(@_)}, + '-' => sub {shift->sub(@_)}, '.' => \&Value::_dot, 'x' => sub {shift->cross(@_)}, '<=>' => sub {shift->compare(@_)}, @@ -159,15 +160,54 @@ sub add { my ($l,$r,$flag) = @_; if ($l->promotePrecedence($r)) {return $r->add($l,!$flag)} - $r = promote($r); - if ($flag) {my $tmp = $l; $l = $r; $r = $tmp} - Value::Error("Intervals can only be added to Intervals, Sets or Unions") - unless Value::class($l) =~ m/Interval|Union|Set/ && - Value::class($r) =~ m/Interval|Union|Set/; + $r = promote($r); if ($flag) {my $tmp = $l; $l = $r; $r = $tmp} return Value::Union->new($l,$r); } sub dot {my $self = shift; $self->add(@_)} +# +# Subtraction can split into a union +# +sub sub { + my ($l,$r,$flag) = @_; + if ($l->promotePrecedence($r)) {return $r->sub($l,!$flag)} + $r = promote($r); if ($flag) {my $tmp = $l; $l = $r; $r = $tmp} + Value::Union::form(subIntervalInterval($l,$r)); +} + +# +# Subtract an interval from another +# (returns the resulting interval(s), set +# or nothing for emtpy set) +# +sub subIntervalInterval { + my ($l,$r) = @_; + my ($a,$b) = $l->value; my ($c,$d) = $r->value; + my @union = (); + if ($d <= $a) { + $l->{open} = '(' if $d == $a && $r->{close} eq ']'; + push(@union,$l) unless $a == $b && $l->{open} eq '('; + } elsif ($c >= $b) { + $l->{close} = ')' if $c == $b && $r->{open} eq '['; + push(@union,$l) unless $a == $b && $l->{close} eq ')'; + } else { + if ($a == $c) { + push(@union,Value::Set->new($a)) + if $l->{open} eq '[' && $r->{open} eq '('; + } elsif ($a < $c) { + my $close = ($r->{open} eq '[')? ')': ']'; + push(@union,Value::Interval->new($l->{open},$a,$c,$close)); + } + if ($d == $b) { + push(@union,Value::Set->new($b)) + if $l->{close} eq ']' && $r->{close} eq ')'; + } elsif ($d < $b) { + my $open = ($r->{close} eq ']') ? '(': '['; + push(@union,Value::Interval->new($open,$d,$b,$l->{close})); + } + } + return @union; +} # # Lexicographic order, but with type of endpoint included @@ -176,8 +216,7 @@ sub compare { my ($l,$r,$flag) = @_; if ($l->promotePrecedence($r)) {return $r->compare($l,!$flag)} - $r = promote($r); - if ($flag) {my $tmp = $l; $l = $r; $r = $tmp}; + $r = promote($r); if ($flag) {my $tmp = $l; $l = $r; $r = $tmp}; my ($la,$lb) = @{$l->data}; my ($ra,$rb) = @{$r->data}; my $cmp = $la <=> $ra; return $cmp if $cmp; $cmp = $l->{open} cmp $r->{open}; return $cmp if $cmp && !$l->{ignoreEndpointTypes}; Index: Union.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Union.pm,v retrieving revision 1.15 retrieving revision 1.16 diff -Llib/Value/Union.pm -Llib/Value/Union.pm -u -r1.15 -r1.16 --- lib/Value/Union.pm +++ lib/Value/Union.pm @@ -9,6 +9,7 @@ use overload '+' => sub {shift->add(@_)}, + '-' => sub {shift->sub(@_)}, '.' => \&Value::_dot, 'x' => sub {shift->cross(@_)}, '<=>' => sub {shift->compare(@_)}, @@ -29,7 +30,7 @@ return $x if $x->type =~ m/Interval|Union|Set/; Value::Error("Formula does not return an Interval, Set or Union"); } - return promote($x); + return $self->new(promote($x)); } Value::Error("Empty unions are not allowed") if scalar(@_) == 0; my @intervals = (); my $isFormula = 0; @@ -68,6 +69,16 @@ } # +# Make a union or interval or set, depending on how +# many there are in the union +# +sub form { + return @_[0] if scalar(@_) == 1; + return Value::Set->new() if scalar(@_) == 0; + $pkg->new(@_); +} + +# # Return the appropriate data. # sub typeRef { @@ -112,16 +123,12 @@ # # -# Addition forms additional unions +# Addition forms unions # sub add { my ($l,$r,$flag) = @_; if ($l->promotePrecedence($r)) {return $r->add($l,!$flag)} - $r = promote($r); - if ($flag) {my $tmp = $l; $l = $r; $r = $tmp} - Value::Error("Unions can only be added to Intervals, Sets or Unions") - unless Value::class($l) =~ m/Interval|Union|Set/ && - Value::class($r) =~ m/Interval|Union|Set/; + $r = promote($r); if ($flag) {my $tmp = $l; $l = $r; $r = $tmp} $l = $pkg->make($l) if ($l->class ne 'Union'); $r = $pkg->make($r) if ($r->class ne 'Union'); return $pkg->make(@{$l->data},@{$r->data}); @@ -129,14 +136,52 @@ sub dot {my $self = shift; $self->add(@_)} # -# @@@ Needs work @@@ +# Subtraction can split intervals into unions +# +sub sub { + my ($l,$r,$flag) = @_; + if ($l->promotePrecedence($r)) {return $r->sub($l,!$flag)} + $r = promote($r); if ($flag) {my $tmp = $l; $l = $r; $r = $tmp} + my $ll = [($l->class eq 'Union')? $l->value: $l]; + my $rr = [($r->class eq 'Union')? $r->value: $r]; + form(subUnionUnion($ll,$rr)); +} + +# +# Which routines to call for the various combinations +# of sets and intervals to do subtraction +# +my %subCall = ( + SetSet => \&Value::Set::subSetSet, + SetInterval => \&Value::Set::subSetInterval, + IntervalSet => \&Value::Set::subIntervalSet, + IntervalInterval => \&Value::Interval::subIntervalInterval, +); + +# +# Subtract a union from another by running through both lists +# and subtracting everything in the second list from everything +# in the first. +# +sub subUnionUnion { + my ($l,$r) = @_; + my @union = (@{$l}); + foreach my $J (@{$r}) { + my @newUnion = (); + foreach my $I (@union) + {push(@newUnion,&{$subCall{$I->type.$J->type}}($I,$J))} + @union = @newUnion; + } + return @union; +} + # # Sort the intervals lexicographically, and then # compare interval by interval. # sub compare { my ($l,$r,$flag) = @_; - if ($l->promotePrecedence($r)) {return $r->add($l,!$flag)} + if ($l->promotePrecedence($r)) {return $r->compare($l,!$flag)} $r = promote($r); if ($flag) {my $tmp = $l; $l = $r; $r = $tmp}; my @l = sort {$a <=> $b} $l->value; my @r = sort {$a <=> $b} $r->value; @@ -161,20 +206,23 @@ } sub string { - my $self = shift; my $equation = shift; - my $context = $equation->{context} || $$Value::context; - my $union = $context->{operators}{'U'}{string} || ' U '; + my $self = shift; my $equation = shift; shift; shift; my $prec = shift; + my $op = ($equation->{context} || $$Value::context)->{operators}{'U'}; my @intervals = (); foreach my $x (@{$self->data}) {push(@intervals,$x->string($equation))} - return join($union,@intervals); + my $string = join($op->{string} || ' U ',@intervals); + $string = '('.$string.')' if $prec > ($op->{precedence} || 1.5); + return $string; } sub TeX { - my $self = shift; my $equation = shift; - my $context = $equation->{context} || $$Value::context; - my @intervals = (); my $op = $context->{operators}{'U'}; + my $self = shift; my $equation = shift; shift; shift; my $prec = shift; + my $op = ($equation->{context} || $$Value::context)->{operators}{'U'}; + my @intervals = (); foreach my $x (@{$self->data}) {push(@intervals,$x->TeX($equation))} - return join($op->{TeX} || $op->{string} || ' U ',@intervals); + my $TeX = join($op->{TeX} || $op->{string} || ' U ',@intervals); + $TeX = '\left('.$TeX.'\right)' if $prec > ($op->{precedence} || 1.5); + return $TeX; } ########################################################################### |
From: Sam H. v. a. <we...@ma...> - 2005-08-11 20:30:40
|
Log Message: ----------- hide options panel here too -- closes bug #802. Modified Files: -------------- webwork2/lib/WeBWorK/ContentGenerator/Instructor: ProblemSetDetail.pm Revision Data ------------- Index: ProblemSetDetail.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm,v retrieving revision 1.19 retrieving revision 1.20 diff -Llib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm -Llib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm -u -r1.19 -r1.20 --- lib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm +++ lib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm @@ -889,6 +889,11 @@ return $text . "is not a plain file!"; } +# don't show view options -- we provide display mode controls for headers/problems separately +sub options { + return ""; +} + # Creates two separate tables, first of the headers, and the of the problems in a given set # If one or more users are specified in the "editForUser" param, only the data for those users # becomes editable, not all the data |
From: Sam H. v. a. <we...@ma...> - 2005-08-11 20:25:25
|
Log Message: ----------- removed view options panel, "try it" link preserves displayMode. closes bug #802. Modified Files: -------------- webwork2/lib/WeBWorK/ContentGenerator/Instructor: SetMaker.pm Revision Data ------------- Index: SetMaker.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm,v retrieving revision 1.49 retrieving revision 1.50 diff -Llib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm -Llib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm -u -r1.49 -r1.50 --- lib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm +++ lib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm @@ -28,6 +28,7 @@ use warnings; use CGI::Pretty qw(); +use WeBWorK::Debug; use WeBWorK::Form; use WeBWorK::Utils qw(readDirectory max sortByName); use WeBWorK::Utils::Tasks qw(renderProblems); @@ -293,10 +294,10 @@ push @active_modes, 'None'; # We have our own displayMode since its value may be None, which is illegal # in other modules. - my $mydisplayMode = $r->param('mydisplayMode') || $r->ce->{pg}->{options}->{displayMode}; - $result .= ' Display Mode: '.CGI::popup_menu(-name=> 'mydisplayMode', + my $displayMode = $r->param('displayMode') || $r->ce->{pg}->{options}->{displayMode}; + $result .= ' Display Mode: '.CGI::popup_menu(-name=> 'displayMode', -values=>\@active_modes, - -default=> $mydisplayMode); + -default=> $displayMode); # Now we give a choice of the number of problems to show my $defaultMax = $r->param('max_shown') || MAX_SHOW_DEFAULT; $result .= ' Max. Shown: '. @@ -819,19 +820,23 @@ problemID=>"1"), params=>{sourceFilePath => "$sourceFileName", problemSeed=> $problem_seed} )}, "Edit it" ); - + + my $displayMode = $self->r->param("displayMode"); + $displayMode = $self->r->ce->{pg}->{options}->{displayMode} + if not defined $displayMode or $displayMode eq "None"; my $try_link = CGI::a({href=>$self->systemLink( $urlpath->newFromModule("WeBWorK::ContentGenerator::Problem", courseID =>$urlpath->arg("courseID"), setID=>"Undefined_Set", problemID=>"1"), - params =>{ - effectiveUser => scalar($self->r->param('user')), - editMode => "SetMaker", - problemSeed=> $problem_seed, - sourceFilePath => "$sourceFileName" - } - )}, "Try it"); + params =>{ + effectiveUser => scalar($self->r->param('user')), + editMode => "SetMaker", + problemSeed=> $problem_seed, + sourceFilePath => "$sourceFileName", + displayMode => $displayMode, + } + )}, "Try it"); my %add_box_data = ( -name=>"trial$cnt",-value=>1,-label=>"Add this problem to the current set on the next update"); if($mark & SUCCESS) { @@ -1205,6 +1210,11 @@ return "Library Browser"; } +# hide view options panel since it distracts from SetMaker's built-in view options +sub options { + return ""; +} + sub body { my ($self) = @_; @@ -1242,7 +1252,7 @@ renderProblems(r=> $r, user => $user, problem_list => [@pg_files[$first_shown..$last_shown]], - displayMode => $r->param('mydisplayMode')) : (); + displayMode => $r->param('displayMode')) : (); ########## Top part print CGI::startform({-method=>"POST", -action=>$r->uri, -name=>'mainform'}), |
From: dpvc v. a. <we...@ma...> - 2005-08-11 14:35:32
|
Log Message: ----------- Changes needed for new Set object, plus for moving string, TeX and perl into Value.pm. Also removed unneeded spaces in perl versions of the constants (these were to fix problems with the minus sign, but that is now being handled by the minus operators themselves). Modified Files: -------------- pg/lib/Parser/Context: Default.pm Revision Data ------------- Index: Default.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser/Context/Default.pm,v retrieving revision 1.27 retrieving revision 1.28 diff -Llib/Parser/Context/Default.pm -Llib/Parser/Context/Default.pm -u -r1.27 -r1.28 --- lib/Parser/Context/Default.pm +++ lib/Parser/Context/Default.pm @@ -87,12 +87,14 @@ }; $lists = { - 'Point' => {class =>'Parser::List::Point'}, - 'Vector' => {class =>'Parser::List::Vector'}, - 'Matrix' => {class =>'Parser::List::Matrix', open => '[', close => ']'}, - 'List' => {class =>'Parser::List::List'}, - 'Interval' => {class =>'Parser::List::Interval'}, - 'AbsoluteValue' => {class =>'Parser::List::AbsoluteValue'}, + 'Point' => {class =>'Parser::List::Point', open => '(', close => ')', separator => ','}, + 'Vector' => {class =>'Parser::List::Vector', open => '<', close => '>', separator => ','}, + 'Matrix' => {class =>'Parser::List::Matrix', open => '[', close => ']', separator => ','}, + 'List' => {class =>'Parser::List::List', open => '(', close => ')', separator => ', '}, + 'Interval' => {class =>'Parser::List::Interval', open => '(', close => ')', separator => ','}, + 'Set' => {class =>'Parser::List::Set', open => '{', close => '}', separator => ','}, + 'Union' => {class =>'Parser::List::Union', open => '', close => '', separator => ' U '}, + 'AbsoluteValue' => {class =>'Parser::List::AbsoluteValue', open => '|', close => '|', separator => ''}, }; $constants = { @@ -222,10 +224,10 @@ ); $fullContext->constants->set( - pi => {TeX => '\pi ', perl => ' pi'}, - i => {isConstant => 1, perl => ' i'}, - j => {TeX => '\boldsymbol{j}', perl => ' j'}, - k => {TeX => '\boldsymbol{k}', perl => ' k'}, + pi => {TeX => '\pi ', perl => 'pi'}, + i => {isConstant => 1, perl => 'i'}, + j => {TeX => '\boldsymbol{j}', perl => 'j'}, + k => {TeX => '\boldsymbol{k}', perl => 'k'}, ); $fullContext->usePrecedence('Standard'); @@ -276,7 +278,7 @@ $vectorContext->variables->are(x=>'Real',y=>'Real',z=>'Real'); $vectorContext->functions->undefine('arg','mod','Re','Im','conj'); $vectorContext->constants->replace(i=>Value::Vector->new(1,0,0)); -$vectorContext->constants->set(i=>{TeX=>'\boldsymbol{i}', perl => ' i'}); +$vectorContext->constants->set(i=>{TeX=>'\boldsymbol{i}', perl=>'i'}); # # Matrix context (square brackets make matrices in preference to points or intervals) @@ -291,7 +293,7 @@ $intervalContext->parens->set( '(' => {type => 'Interval'}, '[' => {type => 'Interval'}, - '{' => {type => 'Interval'}, + '{' => {type => 'Set', removable => 0, emptyOK => 1}, ); ######################################################################### |
From: dpvc v. a. <we...@ma...> - 2005-08-11 14:32:38
|
Log Message: ----------- Changes needt for Set object, and fixed a bug with unions containing constant intervals with non-constant ones. Modified Files: -------------- pg/lib/Parser/BOP: union.pm Revision Data ------------- Index: union.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser/BOP/union.pm,v retrieving revision 1.6 retrieving revision 1.7 diff -Llib/Parser/BOP/union.pm -Llib/Parser/BOP/union.pm -u -r1.6 -r1.7 --- lib/Parser/BOP/union.pm +++ lib/Parser/BOP/union.pm @@ -18,22 +18,26 @@ $self->{type} = Value::Type('Union',2,$Value::Type{number}); $self->{canBeInterval} = 1; foreach my $op ('lop','rop') { - if ($self->{$op}->type !~ m/^(Interval|Union)$/) { - $self->{$op} = bless $self->{$op}, 'Parser::List::Interval'; + if ($self->{$op}->type !~ m/^(Interval|Union|Set)$/) { + if ($self->{$op}->class eq 'Value') { + $self->{$op}{value} = Value::Interval::promote($self->{$op}{value}); + } else { + $self->{$op} = bless $self->{$op}, 'Parser::List::Interval'; + } $self->{$op}->typeRef->{name} = $self->{equation}{context}{parens}{interval}{type}; } } - } else {$self->Error("Operands of '%s' must be intervals",$self->{bop})} + } else {$self->Error("Operands of '%s' must be intervals or sets",$self->{bop})} } # # Make a union of the two operands. # -sub _eval {shift; Value::Union->new(@_)} +sub _eval {$_[1] + $_[2]} # -# Make a union of intervals. +# Make a union of intervals or sets. # sub perl { my $self = shift; my $parens = shift; my @union = (); @@ -44,7 +48,7 @@ } # -# Turn a union into a list of the intervals in the union. +# Turn a union into a list of the intervals or sets in the union. # sub makeUnion { my $self = shift; |
From: dpvc v. a. <we...@ma...> - 2005-08-11 14:31:23
|
Log Message: ----------- Changes needed to Set object. Also, use Parser to handle unions defined as strings rather than doing it by hand (that's the whole point, isn't it?). Added object promotion from lower-precedence classes (for better error messages), and fixed up the comparison routine. Modified Files: -------------- pg/lib/Value: Union.pm Revision Data ------------- Index: Union.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Union.pm,v retrieving revision 1.14 retrieving revision 1.15 diff -Llib/Value/Union.pm -Llib/Value/Union.pm -u -r1.14 -r1.15 --- lib/Value/Union.pm +++ lib/Value/Union.pm @@ -23,25 +23,34 @@ # sub new { my $self = shift; my $class = ref($self) || $self; - @_ = split("U",$_[0]) if scalar(@_) == 1 && !ref($_[0]); - Value::Error("Unions must be of at least two intervals") unless scalar(@_) > 1; + if (scalar(@_) == 1 && !ref($_[0])) { + my $x = Value::makeValue($_[0]); + if (Value::isFormula($x)) { + return $x if $x->type =~ m/Interval|Union|Set/; + Value::Error("Formula does not return an Interval, Set or Union"); + } + return promote($x); + } + Value::Error("Empty unions are not allowed") if scalar(@_) == 0; my @intervals = (); my $isFormula = 0; foreach my $xx (@_) { - my $x = $xx; $x = Value::Interval->new($x) if !ref($x); + my $x = $xx; $x = Value::makeValue($x); if (Value::isFormula($x)) { - $x->{tree}->typeRef->{name} = 'Interval' if ($x->type eq 'Point' && $x->length == 1); - if ($x->type eq 'Interval') {push(@intervals,$x)} + $x->{tree}->typeRef->{name} = 'Interval' + if ($x->type =~ m/Point|List/ && $x->length == 2 && + $x->typeRef->{entryType}{name} eq 'Number'); + if ($x->type =~ m/Interval|Set/) {push(@intervals,$x)} elsif ($x->type eq 'Union') {push(@intervals,$x->{tree}->makeUnion)} - else {Value::Error("Unions can be taken only for Intervals")} + else {Value::Error("Unions can be taken only for Intervals and Sets")} $isFormula = 1; } else { if (Value::class($x) eq 'Point' || Value::class($x) eq 'List') { if ($x->length == 1) {$x = Value::Interval->new('[',$x->value,$x->value,']')} elsif ($x->length == 2) {$x = Value::Interval->new($x->{open},$x->value,$x->{close})} } - if (Value::class($x) eq 'Interval') {push(@intervals,$x)} + if (Value::class($x) =~ m/Interval|Set/) {push(@intervals,$x)} elsif (Value::class($x) eq 'Union') {push(@intervals,@{$x->{data}})} - else {Value::Error("Unions can be taken only for Intervals")} + else {Value::Error("Unions can be taken only for Intervals or Sets")} } } return $self->formula(@intervals) if $isFormula; @@ -49,6 +58,16 @@ } # +# Set the canBeInterval flag +# +sub make { + my $self = shift; + $self = $self->SUPER::make(@_); + $self->{canBeInterval} = 1; + return $self; +} + +# # Return the appropriate data. # sub typeRef { @@ -75,6 +94,18 @@ new($formula,'U',recursiveUnion($formula,@_),$right); } +# +# Try to promote arbitrary data to a set +# +sub promote { + my $x = shift; + return Value::Set->new($x,@_) + if scalar(@_) > 0 || ref($x) eq 'ARRAY' || Value::isRealNumber($x); + return $x if Value::class($x) =~ m/Interval|Union|Set/; + return Value::Interval::promote($x) if Value::class($x) eq 'List'; + Value::Error("Can't convert %s to an Interval, Set or Union",Value::showClass($x)); +} + ############################################ # # Operations on unions @@ -86,15 +117,16 @@ sub add { my ($l,$r,$flag) = @_; if ($l->promotePrecedence($r)) {return $r->add($l,!$flag)} + $r = promote($r); if ($flag) {my $tmp = $l; $l = $r; $r = $tmp} - Value::Error("Unions can only be added to Intervals or Unions") - unless Value::class($l) =~ m/Interval|Union/ && - Value::class($r) =~ m/Interval|Union/; - $l = $pkg->make($l) if ($l->class eq 'Interval'); - $r = $pkg->make($r) if ($r->class eq 'Interval'); + Value::Error("Unions can only be added to Intervals, Sets or Unions") + unless Value::class($l) =~ m/Interval|Union|Set/ && + Value::class($r) =~ m/Interval|Union|Set/; + $l = $pkg->make($l) if ($l->class ne 'Union'); + $r = $pkg->make($r) if ($r->class ne 'Union'); return $pkg->make(@{$l->data},@{$r->data}); } -sub dot {add(@_)} +sub dot {my $self = shift; $self->add(@_)} # # @@@ Needs work @@@ @@ -104,17 +136,15 @@ # sub compare { my ($l,$r,$flag) = @_; + if ($l->promotePrecedence($r)) {return $r->add($l,!$flag)} + $r = promote($r); if ($flag) {my $tmp = $l; $l = $r; $r = $tmp}; - return 1 if Value::class($r) ne 'Union'; - return -1 if Value::class($l) ne 'Union'; - my @l = sort(@{$l->data}); my @r = sort(@{$r->data}); - return scalar(@l) <=> scalar(@r) unless scalar(@l) == scalar(@r); - my $cmp = 0; - foreach my $i (0..$#l) { - $cmp = $l[$i] <=> $r[$i]; - last if $cmp; + my @l = sort {$a <=> $b} $l->value; my @r = sort {$a <=> $b} $r->value; + while (scalar(@l) && scalar(@r)) { + my $cmp = shift(@l) <=> shift(@r); + return $cmp if $cmp; } - return $cmp; + return scalar(@l) - scalar(@r); } # @@@ simplify (combine intervals, if possible) @@@ @@ -124,6 +154,12 @@ # Generate the various output formats # +sub stringify { + my $self = shift; + return $self->TeX if $$Value::context->flag('StringifyAsTeX'); + $self->string; +} + sub string { my $self = shift; my $equation = shift; my $context = $equation->{context} || $$Value::context; |
From: dpvc v. a. <we...@ma...> - 2005-08-11 14:26:21
|
Log Message: ----------- Changes needed for Set object, better handling of an interval given as a string, and a few misc. fixes. Modified Files: -------------- pg/lib/Value: Interval.pm Revision Data ------------- Index: Interval.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Interval.pm,v retrieving revision 1.20 retrieving revision 1.21 diff -Llib/Value/Interval.pm -Llib/Value/Interval.pm -u -r1.20 -r1.21 --- lib/Value/Interval.pm +++ lib/Value/Interval.pm @@ -26,14 +26,17 @@ sub new { my $self = shift; my $class = ref($self) || $self; if (scalar(@_) == 1 && !ref($_[0])) { - my $num = $$Value::context->{pattern}{signedNumber}; - my $inf = $$Value::context->{pattern}{infinite}; - @_ = ($1,$2,$3,$4) if $_[0] =~ m/^ *(\(|\[) *($num|$inf) *, *($num|$inf) *(\)|\]) *$/; + my $x = Value::makeValue($_[0]); + if (Value::isFormula($x)) { + return $x if $x->type eq 'Interval'; + Value::Error("Formula does not return an Interval"); + } + return promote($x); } my ($open,$a,$b,$close) = @_; if (!defined($close)) {$close = $b; $b = $a} Value::Error("Interval() must be called with 3 or 4 arguments") - unless defined($open) && defined($a) && defined($b) && defined($close); + unless defined($open) && defined($a) && defined($b) && defined($close) && scalar(@_) <= 4; $a = Value::makeValue($a); $b = Value::makeValue($b); return $self->formula($open,$a,$b,$close) if Value::isFormula($a) || Value::isFormula($b); Value::Error("Endpoints of intervals must be numbers on infinities") unless @@ -135,9 +138,11 @@ my $x = shift; return $pkg->new($x,@_) if scalar(@_) > 0 || ref($x) eq 'ARRAY'; return $x if ref($x) eq $pkg; + $x = Value::makeValue($x); + return Value::Set->new($x) if Value::class($x) eq 'Real'; my $open = $x->{open}; $open = '(' unless defined($open); my $close = $x->{close}; $close = ')' unless defined($close); - return $pkg->new($open,@{$x->data},$close) + return $pkg->new($open,$x->value,$close) if Value::class($x) =~ m/^(Point|List)$/ && $x->length == 2 && ($open eq '(' || $open eq '[') && ($close eq ')' || $close eq ']'); Value::Error("Can't convert %s to an Interval",Value::showClass($x)); @@ -156,11 +161,12 @@ if ($l->promotePrecedence($r)) {return $r->add($l,!$flag)} $r = promote($r); if ($flag) {my $tmp = $l; $l = $r; $r = $tmp} - Value::Error("Intervals can only be added to Intervals") - unless Value::class($l) eq 'Interval' && Value::class($r) eq 'Interval'; + Value::Error("Intervals can only be added to Intervals, Sets or Unions") + unless Value::class($l) =~ m/Interval|Union|Set/ && + Value::class($r) =~ m/Interval|Union|Set/; return Value::Union->new($l,$r); } -sub dot {add(@_)} +sub dot {my $self = shift; $self->add(@_)} # @@ -174,38 +180,11 @@ if ($flag) {my $tmp = $l; $l = $r; $r = $tmp}; my ($la,$lb) = @{$l->data}; my ($ra,$rb) = @{$r->data}; my $cmp = $la <=> $ra; return $cmp if $cmp; + $cmp = $l->{open} cmp $r->{open}; return $cmp if $cmp && !$l->{ignoreEndpointTypes}; $cmp = $lb <=> $rb; return $cmp if $cmp || $l->{ignoreEndpointTypes}; - $cmp = $l->{open} cmp $r->{open}; return $cmp if $cmp; return $l->{close} cmp $r->{close}; } -############################################ -# -# Generate the various output formats. -# - -sub string { - my $self = shift; my $equation = shift; - my ($a,$b) = @{$self->data}; - $a = $a->string($equation) if Value::isValue($a); - $b = $b->string($equation) if Value::isValue($b); -# return $self->{open}.$a.$self->{close} -# if !$self->{leftInfinte} && !$self->{rightInfinite} && $a == $b; - return $self->{open}.$a.','.$b.$self->{close}; -} - -sub TeX { - my $self = shift; my $equation = shift; - my ($a,$b) = @{$self->data}; - $a = $a->TeX($equation) if Value::isValue($a); - $b = $b->TeX($equation) if Value::isValue($b); - my $open = $self->{open}; my $close = $self->{close}; - $open = '\{' if $open eq '{'; $close = '\}' if $close eq '}'; - $open = '\left'.$open if $open; $close = '\right'.$close if $close; -# return $open.$a.$close if !$self->{leftInfinte} && !$self->{rightInfinite} && $a == $b; - return $open.$a.','.$b.$close; -} - ########################################################################### 1; |
From: dpvc v. a. <we...@ma...> - 2005-08-11 14:24:08
|
Log Message: ----------- Changes needed for new Set object, and moving of string, TeX and perl methods to Value.pm Modified Files: -------------- pg/lib/Value: Formula.pm Point.pm Vector.pm List.pm Revision Data ------------- Index: Vector.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Vector.pm,v retrieving revision 1.18 retrieving revision 1.19 diff -Llib/Value/Vector.pm -Llib/Value/Vector.pm -u -r1.18 -r1.19 --- lib/Value/Vector.pm +++ lib/Value/Vector.pm @@ -179,7 +179,7 @@ return $pkg->make(@coords); } -sub abs {norm(@_)} +sub abs {my $self = shift; $self->norm(@_)} sub norm { my $p = promote(@_)->data; my $s = 0; @@ -249,37 +249,36 @@ sub stringify { my $self = shift; return $self->TeX if $$Value::context->flag('StringifyAsTeX'); - return $self->string(undef,$self->{open},$self->{close}); -}; + $self->string; +} sub string { my $self = shift; my $equation = shift; return $self->ijk($ijk_string) - if ($self->{ijk} || $equation->{ijk} || $$Value::context->flag("ijk")); - my $def = ($equation->{context} || $$Value::context)->lists->get('Vector'); - my $open = shift || $def->{open}; my $close = shift || $def->{close}; - my @coords = (); - foreach my $x (@{$self->data}) { - if (Value::isValue($x)) {push(@coords,$x->string($equation))} else {push(@coords,$x)} - } - return $open.join(',',@coords).$close; + if ($self->{ijk} || $equation->{ijk} || $$Value::context->flag("ijk")) && + !$self->{ColumnVector}; + return $self->SUPER::string($equation,@_); } sub TeX { my $self = shift; my $equation = shift; - return $self->ijk if ($self->{ijk} || $equation->{ijk} || $$Value::context->flag("ijk")); - my $def = ($equation->{context} || $$Value::context)->lists->get('Vector'); - my $open = shift || $self->{open} || $def->{open}; - my $close = shift || $self->{close} || $def->{close}; - my @coords = (); - foreach my $x (@{$self->data}) { - if (Value::isValue($x)) {push(@coords,$x->TeX($equation))} else {push(@coords,$x)} + if ($self->{ColumnVector}) { + my $def = ($equation->{context} || $$Value::context)->lists->get('Matrix'); + my $open = shift; my $close = shift; + $open = $self->{open} unless defined($open); + $open = $def->{open} unless defined($open); + $close = $self->{close} unless defined($close); + $close = $def->{close} unless defined($close); + $open =~ s/([{}])/\\$1/g; $close =~ s/([{}])/\\$1/g; + $open = '\left'.$open if $open; $close = '\right'.$close if $close; + my @coords = (); + foreach my $x (@{$self->data}) { + if (Value::isValue($x)) {push(@coords,$x->TeX($equation))} else {push(@coords,$x)} + } + return $open.'\begin{array}{c}'.join('\\\\',@coords).'\\\\\end{array}'.$close; } - return '\left'.$open.join(',',@coords).'\right'.$close unless $self->{ColumnVector}; - $def = ($equation->{context} || $$Value::context)->lists->get('Matrix'); - $open = shift || $self->{open} || $def->{open}; - $close = shift || $self->{close} || $def->{close}; - return '\left'.$open.'\begin{array}{c}'.join('\\\\',@coords).'\\\\\end{array}\right'.$close; + return $self->ijk if ($self->{ijk} || $equation->{ijk} || $$Value::context->flag("ijk")); + return $self->SUPER::TeX($equation,@_) unless $self->{ColumnVector}; } sub ijk { @@ -289,9 +288,9 @@ unless (scalar(@coords) <= 3); my $string = ''; my $n; my $term; foreach $n (0..scalar(@coords)-1) { - $term = $coords[$n]; - if ($term != 0) { - $term = '' if $term == 1; $term = '-' if $term == -1; + $term = $coords[$n]; $term = (Value::isValue($term))? $term->string : "$term"; + if ($term ne 0) { + $term = '' if $term eq '1'; $term = '-' if $term eq '-1'; $term = '('.$term.')' if $term =~ m/e/i; $term = '+' . $term unless $string eq '' or $term =~ m/^-/; $string .= $term . $ijk->[$n]; Index: List.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/List.pm,v retrieving revision 1.16 retrieving revision 1.17 diff -Llib/Value/List.pm -Llib/Value/List.pm -u -r1.16 -r1.17 --- lib/Value/List.pm +++ lib/Value/List.pm @@ -81,7 +81,7 @@ ($l,$r) = (promote($l)->data,promote($r)->data); return $pkg->new(@{$l},@{$r}); } -sub dot {add(@_)} +sub dot {my $self = shift; $self->add(@_)} # # Lexicographic compare @@ -99,51 +99,6 @@ return scalar(@{$l}) <=> scalar(@{$r}); } -############################################ -# -# Generate the various output formats. -# - -sub stringify { - my $self = shift; - return $self->TeX() if $$Value::context->flag('StringifyAsTeX'); - my $open = $self->{open}; my $close = $self->{close}; - $open = $$Value::context->lists->get('List')->{open} unless defined($open); - $close = $$Value::context->lists->get('List')->{close} unless defined($close); - $open.join(', ',@{$self->data}).$close; -} - -sub string { - my $self = shift; my $equation = shift; - my $def = ($equation->{context} || $$Value::context)->lists->get('List'); - my $open = shift; my $close = shift; - $open = $def->{open} unless defined($open); - $close = $def->{close} unless defined($close); - my @coords = (); - foreach my $x (@{$self->data}) { - if (Value::isValue($x)) - {push(@coords,$x->string($equation))} else {push(@coords,$x)} - } - return $open.join(', ',@coords).$close; -} -sub TeX { - my $self = shift; my $equation = shift; - my $context = $equation->{context} || $$Value::context; - my $def = $context->lists->get('List'); - my $open = shift; my $close = shift; - $open = $def->{open} unless defined($open); - $close = $def->{close} unless defined($close); - $open = '\{' if $open eq '{'; $close = '\}' if $close eq '}'; - $open = '\left'.$open if $open; $close = '\right'.$close if $close; - my @coords = (); my $str = $context->{strings}; - foreach my $x (@{$self->data}) { - if (Value::isValue($x)) {push(@coords,$x->TeX($equation))} - elsif (defined($str->{$x}) && $str->{$x}{TeX}) {push(@coords,$str->{$x}{TeX})} - else {push(@coords,$x)} - } - return $open.join(',',@coords).$close; -} - ########################################################################### 1; Index: Formula.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Formula.pm,v retrieving revision 1.31 retrieving revision 1.32 diff -Llib/Value/Formula.pm -Llib/Value/Formula.pm -u -r1.31 -r1.32 --- lib/Value/Formula.pm +++ lib/Value/Formula.pm @@ -94,7 +94,7 @@ $l = $parser->{Value}->new($formula,$l) unless ref($l) =~ m/^Parser::/; $r = $parser->{Value}->new($formula,$r) unless ref($r) =~ m/^Parser::/; $bop = 'U' if $bop eq '+' && - ($l->type =~ m/Interval|Union/ || $r->type =~ m/Interval|Union/); + ($l->type =~ m/Interval|Union|Set/ || $r->type =~ m/Interval|Union|Set/); $formula->{tree} = $parser->{BOP}->new($formula,$bop,$l,$r); $formula->{variables} = $formula->{tree}->getVariables; return $formula->eval if scalar(%{$formula->{variables}}) == 0; @@ -485,6 +485,16 @@ # sub isConstant {scalar(%{shift->{variables}}) == 0} +############################################ +# +# Provide output formats +# +sub stringify { + my $self = shift; + return $self->TeX if $$Value::context->flag('StringifyAsTeX'); + $self->string; +} + ########################################################################### 1; Index: Point.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Point.pm,v retrieving revision 1.16 retrieving revision 1.17 diff -Llib/Value/Point.pm -Llib/Value/Point.pm -u -r1.16 -r1.17 --- lib/Value/Point.pm +++ lib/Value/Point.pm @@ -168,42 +168,6 @@ return CORE::sqrt($s); } - -############################################ -# -# Generate the various output formats -# - -sub stringify { - my $self = shift; - return $self->TeX if $$Value::context->flag('StringifyAsTeX'); - return $self->string(undef,$self->{open},$self->{close}); -} - -sub string { - my $self = shift; my $equation = shift; - my $def = ($equation->{context} || $$Value::context)->lists->get('Point'); - my $open = shift || $def->{open}; my $close = shift || $def->{close}; - my @coords = (); - foreach my $x (@{$self->data}) { - if (Value::isValue($x)) {push(@coords,$x->string($equation))} else {push(@coords,$x)} - } - return $open.join(',',@coords).$close; -} - -sub TeX { - my $self = shift; my $equation = shift; - my $def = ($equation->{context} || $$Value::context)->lists->get('Point'); - my $open = shift || $self->{open} || $def->{open}; - my $close = shift || $self->{close} || $def->{close}; - my @coords = (); - foreach my $x (@{$self->data}) { - if (Value::isValue($x)) {push(@coords,$x->TeX($equation))} else {push(@coords,$x)} - } - return '\left'.$open.join(',',@coords).'\right'.$close; -} - ########################################################################### 1; - |
From: dpvc v. a. <we...@ma...> - 2005-08-11 14:21:32
|
Log Message: ----------- Added changes needed for the new Set object. Modified Files: -------------- pg/lib/Value: AnswerChecker.pm Revision Data ------------- Index: AnswerChecker.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/AnswerChecker.pm,v retrieving revision 1.50 retrieving revision 1.51 diff -Llib/Value/AnswerChecker.pm -Llib/Value/AnswerChecker.pm -u -r1.50 -r1.51 --- lib/Value/AnswerChecker.pm +++ lib/Value/AnswerChecker.pm @@ -188,7 +188,7 @@ my $self = shift; my $ans = shift; my $class = $self->showClass; $class =~ s/Real //; return $class if $class =~ m/Formula/; - return "an Interval or Union" if $class =~ m/Interval/i; + return "an Interval, Set or Union" if $class =~ m/Interval|Set|Union/i; return $class; } @@ -770,7 +770,7 @@ ($other->{open} eq '(' || $other->{open} eq '[') && ($other->{close} eq ')' || $other->{close} eq ']') if $other->type =~ m/^(Point|List)$/; - $other->type =~ m/^(Interval|Union)$/; + $other->type =~ m/^(Interval|Union|Set)$/; } sub cmp_compare { @@ -807,6 +807,46 @@ ############################################################# +package Value::Set; + +sub typeMatch { + my $self = shift; my $other = shift; + return 0 unless ref($other) && $other->class ne 'Formula'; + return $other->length == 2 && + ($other->{open} eq '(' || $other->{open} eq '[') && + ($other->{close} eq ')' || $other->{close} eq ']') + if $other->type =~ m/^(Point|List)$/; + $other->type =~ m/^(Interval|Union|Set)/; +} + +# +# Use the List checker for sets, in order to get +# partial credit. Set the various types for error +# messages. +# +sub cmp_defaults {( + Value::List::cmp_defaults(@_), + typeMatch => 'Value::Real', + list_type => 'a set', + entry_type => 'a number', + removeParens => 0, + showParenHints => 1, +)} + +# +# Use the list checker if the student answer is a set +# otherwise use the standard compare (to get better +# error messages +# +sub cmp_equal { + my ($self,$ans) = @_; + Value::List::cmp_equal(@_) + if $ans->{student_value}->type eq 'Set'; + Value::cmp_equal(@_); +} + +############################################################# + package Value::Union; sub typeMatch { @@ -816,7 +856,7 @@ ($other->{open} eq '(' || $other->{open} eq '[') && ($other->{close} eq ')' || $other->{close} eq ']') if $other->type =~ m/^(Point|List)$/; - $other->type =~ m/^(Interval|Union)/; + $other->type =~ m/^(Interval|Union|Set)/; } # @@ -827,8 +867,9 @@ sub cmp_defaults {( Value::List::cmp_defaults(@_), typeMatch => 'Value::Interval', - list_type => 'an interval or union', - entry_type => 'an interval', + list_type => 'an interval, set or union', + short_type => 'a union', + entry_type => 'an interval or set', )} sub cmp_equal {Value::List::cmp_equal(@_)} @@ -887,18 +928,19 @@ # my $showHints = getOption($ans,'showHints'); my $showLengthHints = getOption($ans,'showLengthHints'); - my $showParenHints = getOption($ans,'showLengthHints'); + my $showParenHints = getOption($ans,'showParenHints'); my $partialCredit = getOption($ans,'partialCredit'); my $requireParenMatch = $ans->{requireParenMatch}; my $typeMatch = $ans->{typeMatch}; my $value = $ans->{entry_type}; my $ltype = $ans->{list_type} || lc($self->type); + my $stype = $ans->{short_type} || $ltype; $value = (Value::isValue($typeMatch)? lc($typeMatch->cmp_class): 'value') unless defined($value); $value =~ s/(real|complex) //; $ans->{cmp_class} = $value; $value =~ s/^an? //; $value = 'formula' if $value =~ m/formula/; - $ltype =~ s/^an? //; + $ltype =~ s/^an? //; $stype =~ s/^an? //; $showHints = $showLengthHints = 0 if $ans->{isPreview}; # @@ -932,7 +974,7 @@ if (($cOpen || $cClose) && ($sOpen || $sClose)) {$message .= "are of the wrong type"} elsif ($sOpen || $sClose) {$message .= "should be removed"} - else {$message .= "are missing"} + else {$message .= "seem to be missing"} $self->cmp_Error($ans,$message) unless $ans->{isPreview}; } return; @@ -966,9 +1008,9 @@ # if ($showLengthHints) { $value =~ s/ or /s or /; # fix "interval or union" - push(@errors,"There should be more ${value}s in your $ltype") + push(@errors,"There should be more ${value}s in your $stype") if ($score < $maxscore && $score == $m); - push(@errors,"There should be fewer ${value}s in your $ltype") + push(@errors,"There should be fewer ${value}s in your $stype") if ($score < $maxscore && $score == $M && !$showHints); } @@ -1061,8 +1103,8 @@ sub splitFormula { my $self = shift; my $formula = shift; my $ans = shift; my @formula; my @entries; - if ($formula->type eq 'List') {@entries = @{$formula->{tree}{coords}}} - else {@entries = $formula->{tree}->makeUnion} + if ($formula->type eq 'Union') {@entries = $formula->{tree}->makeUnion} + else {@entries = @{$formula->{tree}{coords}}} foreach my $entry (@entries) { my $v = Parser::Formula($entry); $v = Parser::Evaluate($v) if (defined($v) && $v->isConstant); |
From: dpvc v. a. <we...@ma...> - 2005-08-11 14:20:55
|
Log Message: ----------- Added in the Set object class. Also made more comprehensive defaults for string, TeX and perl output, so these don't have to be subclassed so often. These handle looking up the open and close parens and recursively handling the entries in a list-type object. Modified Files: -------------- pg/lib: Value.pm Revision Data ------------- Index: Value.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value.pm,v retrieving revision 1.40 retrieving revision 1.41 diff -Llib/Value.pm -Llib/Value.pm -u -r1.40 -r1.41 --- lib/Value.pm +++ lib/Value.pm @@ -16,6 +16,7 @@ 'Vector' => {open => '<', close => '>'}, 'Matrix' => {open => '[', close => ']'}, 'List' => {open => '(', close => ')'}, + 'Set' => {open => '{', close => '}'}, }, flags => { # @@ -64,10 +65,11 @@ 'Matrix' => 6, 'List' => 7, 'Interval' => 8, - 'Union' => 9, - 'String' => 10, - 'Formula' => 11, - 'special' => 12, + 'Set' => 9, + 'Union' => 10, + 'String' => 11, + 'Formula' => 12, + 'special' => 20, }; # @@ -456,10 +458,51 @@ sub stringify { my $self = shift; return $self->TeX() if $$Value::context->flag('StringifyAsTeX'); - $self->string; + my $def = $$Value::context->lists->get($self->class); + return $self->string unless $def; + my $open = $self->{open}; $open = $def->{open} unless defined($open); + my $close = $self->{close}; $close = $def->{close} unless defined($close); + $open.join($def->{separator},@{$self->data}).$close; +} + +sub string { + my $self = shift; my $equation = shift; + my $def = ($equation->{context} || $$Value::context)->lists->get($self->class); + return $self->value unless $def; + my $open = shift; my $close = shift; + $open = $self->{open} unless defined($open); + $open = $def->{open} unless defined($open); + $close = $self->{close} unless defined($close); + $close = $def->{close} unless defined($close); + my @coords = (); + foreach my $x (@{$self->data}) { + if (Value::isValue($x)) + {push(@coords,$x->string($equation))} else {push(@coords,$x)} + } + return $open.join($def->{separator},@coords).$close; } -sub string {shift->value} -sub TeX {shift->string(@_)} + +sub TeX { + my $self = shift; my $equation = shift; + my $context = $equation->{context} || $$Value::context; + my $def = $context->lists->get($self->class); + return $self->string(@_) unless $def; + my $open = shift; my $close = shift; + $open = $self->{open} unless defined($open); + $open = $def->{open} unless defined($open); + $close = $self->{close} unless defined($close); + $close = $def->{close} unless defined($close); + $open =~ s/([{}])/\\$1/g; $close =~ s/([{}])/\\$1/g; + $open = '\left'.$open if $open; $close = '\right'.$close if $close; + my @coords = (); my $str = $context->{strings}; + foreach my $x (@{$self->data}) { + if (Value::isValue($x)) {push(@coords,$x->TeX($equation))} + elsif (defined($str->{$x}) && $str->{$x}{TeX}) {push(@coords,$str->{$x}{TeX})} + else {push(@coords,$x)} + } + return $open.join(',',@coords).$close; +} + # # For perl, call the appropriate constructor around the object's data # @@ -541,6 +584,7 @@ use Value::Matrix; use Value::List; use Value::Interval; +use Value::Set; use Value::Union; use Value::String; use Value::Formula; |
From: dpvc v. a. <we...@ma...> - 2005-08-11 14:18:49
|
Log Message: ----------- Properly maintain open and close parens in emtpy lists. Modified Files: -------------- pg/lib: Parser.pm Revision Data ------------- Index: Parser.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser.pm,v retrieving revision 1.27 retrieving revision 1.28 diff -Llib/Parser.pm -Llib/Parser.pm -u -r1.27 -r1.28 --- lib/Parser.pm +++ lib/Parser.pm @@ -323,7 +323,7 @@ /open/ and do { my $top = $self->pop; my $paren = $parens->{$top->{value}}; if ($paren->{emptyOK} && $paren->{close} eq $type) { - $self->pushOperand($parser->{List}->new($self,[],1,$paren)) + $self->pushOperand($parser->{List}->new($self,[],1,$paren,undef,$top->{value},$paren->{close})) } elsif ($type eq 'start') {$self->Error(["Missing close parenthesis for '%s'",$top->{value}],$top->{ref})} elsif ($top->{value} eq 'start') {$self->Error(["Extra close parenthesis '%s'",$type],$ref)} |
From: dpvc v. a. <we...@ma...> - 2005-08-11 14:17:48
|
Log Message: ----------- Added new Set object class to the Parser. It implements a finite set of real numbers, for use with unions and intervals. E.g., (1,2) U {3} or (1,2) U {3,4,5}. You can created Set objects in your perl code via the Set() command, e.g, Set(3,4,5) or Set("{1,2,3}"). You should set the Context to Context("Interval") if you plan to use Set objects, as this defined the braces to form sets (rather than using them as parentheses, which is the default WW behavior). Note that in Interval context, you can NOT use braces as parentheses. Current, Set objects are only allowed to be sets of numbers. It would be possible to extend that in the future. Modified Files: -------------- pg/macros: Value.pl pg/lib/Parser: List.pm Value.pm Added Files: ----------- pg/lib/Parser/List: Set.pm pg/lib/Value: Set.pm Revision Data ------------- --- /dev/null +++ lib/Parser/List/Set.pm @@ -0,0 +1,28 @@ +######################################################################### +# +# Implements the Set class +# +package Parser::List::Set; +use strict; use vars qw(@ISA); +@ISA = qw(Parser::List); + +# +# Check that the entries are numbers. +# +sub _check { + my $self = shift; + foreach my $x (@{$self->{coords}}) { + $self->Error("Sets can't contain infinity") if $x->{isInfinite}; + $self->Error("Entries in a set must be real numbers") unless $x->isRealNumber; + } +} + +sub checkInterval { + my $self = shift; + $self->{canBeInterval} = 1; +} + +######################################################################### + +1; + --- /dev/null +++ lib/Value/Set.pm @@ -0,0 +1,129 @@ +########################################################################### + +package Value::Set; +my $pkg = 'Value::Set'; + +use strict; +use vars qw(@ISA); +@ISA = qw(Value); + +use overload + '+' => sub {shift->add(@_)}, + '.' => \&Value::_dot, + 'x' => sub {shift->cross(@_)}, + '<=>' => sub {shift->compare(@_)}, + 'cmp' => sub {shift->compare_string(@_)}, + 'nomethod' => sub {shift->nomethod(@_)}, + '""' => sub {shift->stringify(@_)}; + +# Convert a value to a Set. The value can be +# a list of numbers, or an reference to an array of numbers +# a point, vector or set object +# a matrix if it is n x 1 or 1 x n +# a string that evaluates to a point +# +sub new { + my $self = shift; my $class = ref($self) || $self; + my $p = shift; $p = [$p,@_] if (scalar(@_) > 0); + $p = Value::makeValue($p) if (defined($p) && !ref($p)); + return $p if (Value::isFormula($p) && $p->type eq Value::class($self)); + my $pclass = Value::class($p); my $isFormula = 0; + my @d; @d = $p->dimensions if $pclass eq 'Matrix'; + if ($pclass =~ m/Point|Vector|Set/) {$p = $p->data} + elsif ($pclass eq 'Matrix' && scalar(@d) == 1) {$p = [$p->value]} + elsif ($pclass eq 'Matrix' && scalar(@d) == 2 && $d[0] == 1) {$p = ($p->value)[0]} + elsif ($pclass eq 'Matrix' && scalar(@d) == 2 && $d[1] == 1) {$p = ($p->transpose->value)[0]} + else { + $p = [$p] if (defined($p) && ref($p) ne 'ARRAY'); + foreach my $x (@{$p}) { + $x = Value::makeValue($x); + $isFormula = 1 if Value::isFormula($x); + Value::Error("An element of sets can't be %s",Value::showClass($x)) + unless Value::isRealNumber($x); + } + } + return $self->formula($p) if $isFormula; + my $def = $$Value::context->lists->get('Set'); + bless { + data => $p, canBeInterval => 1, + open => $def->{open}, close => $def->{close} + }, $class; +} + +# +# Set the canBeInterval flag +# +sub make { + my $self = shift; my $def = $$Value::context->lists->get('Set'); + $self = $self->SUPER::make(@_); + $self->{canBeInterval} = 1; + $self->{open} = $def->{open}; $self->{close} = $def->{close}; + return $self; +} + +sub isOne {0} +sub isZero {0} + +# +# Try to promote arbitrary data to a set +# +sub promote { + my $x = shift; + return $pkg->new($x,@_) + if scalar(@_) > 0 || ref($x) eq 'ARRAY' || Value::isRealNumber($x); + return $x if Value::class($x) =~ m/Interval|Union|Set/; + Value::Error("Can't convert %s to a Set",Value::showClass($x)); +} + +############################################ +# +# Operations on sets +# + +# +# Addition forms additional sets +# +sub add { + my ($l,$r,$flag) = @_; + if ($l->promotePrecedence($r)) {return $r->add($l,!$flag)} + $r = promote($r); + if ($flag) {my $tmp = $l; $l = $r; $r = $tmp} + Value::Error("Sets can only be added to Intervals, Sets or Unions") + unless Value::class($l) =~ m/Interval|Union|Set/ && + Value::class($r) =~ m/Interval|Union|Set/; + return Value::Union->new($l,$r) + unless Value::class($l) eq 'Set' && Value::class($r) eq 'Set'; + my @combined = (sort {$a <=> $b} (@{$l->data},@{$r->data})); + my @entries = (); + while (scalar(@combined)) { + push(@entries,shift(@combined)); + shift(@combined) while (scalar(@combined) && $entries[-1] == $combined[0]); + } + return $pkg->make(@entries); +} +sub dot {my $self = shift; $self->add(@_)} + +sub compare { + my ($l,$r,$flag) = @_; + if ($l->promotePrecedence($r)) {return $r->compare($l,!$flag)} + $r = promote($r); + if ($r->class eq 'Interval') { + return ($flag? 1: -1) if $l->length == 0; + my ($a,$b) = $r->value; my $c = $l->{data}[0]; + return (($flag) ? $a <=> $c : $c <=> $a) + if ($l->length == 1 && $a == $b) || $a != $c; + return ($flag? 1: -1); + } + if ($flag) {my $tmp = $l; $l = $r; $r = $tmp}; + my @l = sort {$a <=> $b} @{$l->data}; my @r = sort {$a <=> $b} @{$r->data}; + while (scalar(@l) && scalar(@r)) { + my $cmp = shift(@l) <=> shift(@r); + return $cmp if $cmp; + } + return scalar(@l) - scalar(@r); +} + +########################################################################### + +1; + Index: Value.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/Value.pl,v retrieving revision 1.6 retrieving revision 1.7 diff -Lmacros/Value.pl -Lmacros/Value.pl -u -r1.6 -r1.7 --- macros/Value.pl +++ macros/Value.pl @@ -14,6 +14,7 @@ sub Matrix {Value::Matrix->new(@_)} sub List {Value::List->new(@_)} sub Interval {Value::Interval->new(@_)} +sub Set {Value::Set->new(@_)} sub Union {Value::Union->new(@_)} sub ColumnVector {Value::Vector->new(@_)->with(ColumnVector=>1,open=>undef,close=>undef)} Index: Value.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser/Value.pm,v retrieving revision 1.13 retrieving revision 1.14 diff -Llib/Parser/Value.pm -Llib/Parser/Value.pm -u -r1.13 -r1.14 --- lib/Parser/Value.pm +++ lib/Parser/Value.pm @@ -37,7 +37,10 @@ value => $value, type => $type, isConstant => 1, ref => $ref, equation => $equation, }, $class; - $c->{canBeInterval} = 1 if ($value->class eq 'Point' && $type->{length} == 2); + $c->{canBeInterval} = 1 + if $value->{canBeInterval} || + ($value->class =~ m/Point|List/ && + $type->{length} == 2 && $type->{entryType}{name} eq 'Number'); $c->{isZero} = $value->isZero; $c->{isOne} = $value->isOne; @@ -78,7 +81,7 @@ sub perl { my $self = shift; my $parens = shift; my $matrix = shift; my $perl = $self->{value}->perl(0,$matrix); - $perl = 'Closed('.$perl.')' + $perl = "(($perl)->with(open=>'$self->{open}',close=>'$self->{close}'))" if $self->{canBeInterval} && $self->{open}.$self->{close} eq '[]'; $perl = '('.$perl.')' if $parens; return $perl; @@ -99,7 +102,7 @@ # # Get a Union object's data # -sub makeUnion {@{shift->{value}->{data}}} +sub makeUnion {@{shift->{value}{data}}} ######################################################################### Index: List.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser/List.pm,v retrieving revision 1.14 retrieving revision 1.15 diff -Llib/Parser/List.pm -Llib/Parser/List.pm -u -r1.14 -r1.15 --- lib/Parser/List.pm +++ lib/Parser/List.pm @@ -27,7 +27,7 @@ my $open = shift || ''; my $close = shift || ''; my $context = $equation->{context}; my $parens = $context->{parens}; my $list; - + if ($paren && $close && $paren->{formInterval}) { $paren = $parens->{interval} if ($paren->{close} ne $close || (scalar(@{$coords}) == 2 && @@ -36,7 +36,7 @@ } my $type = Value::Type($paren->{type},scalar(@{$coords}),$entryType, list => 1, formMatrix => $paren->{formMatrix}); - if ($type->{name} ne 'Interval') { + if ($type->{name} ne 'Interval' && ($type->{name} ne 'Set' || $type->{length} != 0)) { if ($paren->{formMatrix} && $entryType->{formMatrix}) {$type->{name} = 'Matrix'} elsif ($entryType->{name} eq 'unknown') { if ($paren->{formList}) {$type->{name} = 'List'} @@ -228,7 +228,7 @@ my $perl; my @p = (); foreach my $x (@{$self->{coords}}) {push(@p,$x->perl)} $perl = 'new Value::'.$self->type.'('.join(',',@p).')'; - $perl = 'Closed('.$perl.')' + $perl = "${perl}->with(open=>'$self->{open}',close=>'$self->{close}')" if $self->{canBeInterval} && $self->{open}.$self->{close} eq '[]'; $perl = '('.$perl.')' if $parens; return $perl; @@ -244,6 +244,7 @@ use Parser::List::Matrix; use Parser::List::List; use Parser::List::Interval; +use Parser::List::Set; use Parser::List::AbsoluteValue; ######################################################################### |
From: dpvc v. a. <we...@ma...> - 2005-08-11 00:36:57
|
Log Message: ----------- Make upper-case alias a hidden one (since it is not needed for the pattern). Modified Files: -------------- pg/lib/Parser/Context: Strings.pm Revision Data ------------- Index: Strings.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser/Context/Strings.pm,v retrieving revision 1.6 retrieving revision 1.7 diff -Llib/Parser/Context/Strings.pm -Llib/Parser/Context/Strings.pm -u -r1.6 -r1.7 --- lib/Parser/Context/Strings.pm +++ lib/Parser/Context/Strings.pm @@ -70,7 +70,7 @@ } # -# Add lower-case alias for case-insensitive strings +# Add upper-case alias for case-insensitive strings # (so we can always find their definitions) # sub add { @@ -79,7 +79,7 @@ $self->SUPER::add(@_); my %D = (@_); foreach my $x (keys %D) { - $data->{uc($x)} = {alias => $x} + $data->{uc($x)} = {alias => $x, hidden => 1} unless $data->{$x}{caseSensitive} || uc($x) eq $x; } } |
From: dpvc v. a. <we...@ma...> - 2005-08-11 00:35:51
|
Log Message: ----------- Dereference aliases to aliases properly. Modified Files: -------------- pg/lib/Parser: String.pm Revision Data ------------- Index: String.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser/String.pm,v retrieving revision 1.9 retrieving revision 1.10 diff -Llib/Parser/String.pm -Llib/Parser/String.pm -u -r1.9 -r1.10 --- lib/Parser/String.pm +++ lib/Parser/String.pm @@ -22,7 +22,7 @@ $def = undef if $def->{caseSensitive} && $value ne uc($value); } $value = $def->{alias}, $def = $equation->{context}{strings}{$value} - if defined($def->{alias}); + while defined($def->{alias}); my $str = bless { value => $value, type => $Value::Type{string}, isConstant => 1, def => $def, ref => $ref, equation => $equation, |
From: jj v. a. <we...@ma...> - 2005-08-10 23:03:58
|
Log Message: ----------- Default behavior of number_list_cmp set to be backward compatible (no hints of any kind or partial credit). These features can now be activated by optional arguments. Also improvements on parser-based interval_cmp. Modified Files: -------------- pg/macros: extraAnswerEvaluators.pl Revision Data ------------- Index: extraAnswerEvaluators.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/extraAnswerEvaluators.pl,v retrieving revision 1.7 retrieving revision 1.8 diff -Lmacros/extraAnswerEvaluators.pl -Lmacros/extraAnswerEvaluators.pl -u -r1.7 -r1.8 --- macros/extraAnswerEvaluators.pl +++ macros/extraAnswerEvaluators.pl @@ -626,21 +626,25 @@ my $oldContext = Context(); my ($context, $ans_eval); if(defined($opts{unions}) and $opts{unions} eq 'no' ) { - # This is really a list of points - $context = Context("Vector")->copy; + # This is really a list of points, not intervals at all + $context = $Parser::Context::Default::context{Vector}->copy; $ans_type = 'List'; $options{showCoordinateHints} = 0; $options{showHints} = 0; $options{partialCredit}=0; $options{showLengthHints} = 0; } else { - $context = Context("Numeric")->copy; + $context = $Parser::Context::Default::context{Numeric}->copy; + $context->parens->set( + '(' => {type => 'Interval'}, + '[' => {type => 'Interval'}, + '{' => {type => 'Interval'}, + ); $correct_ans =~ tr/u/U/; if($correct_ans =~ /U/) { $context->operators->add('u'=> {precedence => 0.5, associativity => 'left', type => 'bin', isUnion => 1, string => ' U ', TeX => '\cup ', class => 'Parser::BOP::union'}); -# $context->operators->add('u'=> {alias => 'U'}); $ans_type = 'Union'; $options{showHints} = 0; $options{showLengthHints} = 0; @@ -676,23 +680,30 @@ if (defined($opts{'sloppy'}) && $opts{'sloppy'} eq 'yes') { $options{requireParenMatch} = 0; } + $context->strings->add( + 'i' => {alias=>'infinity'}, + 'infty' => {alias=>'infinity'}, + 'minfinity' => {infinite=>1, negative=>1}, + 'minfty' => {alias=>'minfinity'}, + 'minf' => {alias=>'minfinity'}, + 'mi' => {alias=>'minfinity'}, + ); Context($context); if($ans_type eq 'List') { $ans_eval = List($correct_ans)->cmp(%options); } elsif($ans_type eq 'Union') { $ans_eval = Union($correct_ans)->cmp(%options); - warn "Union with options ".join(',', %options); } elsif($ans_type eq 'Interval') { $ans_eval = Interval($correct_ans)->cmp(%options); } else { - warn "Bug -- should not be here"; + warn "Bug -- should not be here in interval_cmp"; } Context($oldContext); return($ans_eval); - # ToDo: tolerances + # ToDo: # modes? # strings # infinities @@ -787,7 +798,7 @@ } $context->{format}{number} = $num_params{'format'} || $main::numFormatDefault; $context->strings->clear; - if (defined($num_params{strings}) && $num_params{strings}) { + 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}); @@ -816,6 +827,11 @@ zeroLevelTol => $num_params{zeroLevelTol}, ); $options{ordered} = 1 if(defined($num_params{ordered}) and $opts{ordered}); + # These didn't exist before in number_list_cmp so they behaved like + # in List()->cmp. Now they can be optionally set + $options{showHints}= $num_params{showHints} || 0; + $options{showLengthHints}= $num_params{showHints} || 0; + $options{partialCredit}= $num_params{showHints} || 0; Context($context); my $ans_eval = List($list)->cmp(%options); |
From: jj v. a. <we...@ma...> - 2005-08-10 18:55:33
|
Log Message: ----------- Hopefully improved some of how the temporary context is set for number_list_cmp. Many whitespace changes (spaces to tabs). Work in progress for a version of interval_cmp which uses Parser, temporarily called interval_cmp2. Modified Files: -------------- pg/macros: extraAnswerEvaluators.pl Revision Data ------------- Index: extraAnswerEvaluators.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/extraAnswerEvaluators.pl,v retrieving revision 1.6 retrieving revision 1.7 diff -Lmacros/extraAnswerEvaluators.pl -Lmacros/extraAnswerEvaluators.pl -u -r1.6 -r1.7 --- macros/extraAnswerEvaluators.pl +++ macros/extraAnswerEvaluators.pl @@ -611,6 +611,98 @@ =cut +sub interval_cmp2 { + my $correct_ans = shift; + + my %opts = @_; + + my $mode = $num_params{mode} || 'std'; + my %options = (debug => $opts{debug}); + my $ans_type = ''; # set to List, Union, or Interval below + + # + # Get an apppropriate context based on the mode + # + my $oldContext = Context(); + my ($context, $ans_eval); + if(defined($opts{unions}) and $opts{unions} eq 'no' ) { + # This is really a list of points + $context = Context("Vector")->copy; + $ans_type = 'List'; + $options{showCoordinateHints} = 0; + $options{showHints} = 0; + $options{partialCredit}=0; + $options{showLengthHints} = 0; + } else { + $context = Context("Numeric")->copy; + $correct_ans =~ tr/u/U/; + if($correct_ans =~ /U/) { + $context->operators->add('u'=> {precedence => 0.5, associativity => 'left', + type => 'bin', isUnion => 1, string => ' U ', TeX => '\cup ', + class => 'Parser::BOP::union'}); +# $context->operators->add('u'=> {alias => 'U'}); + $ans_type = 'Union'; + $options{showHints} = 0; + $options{showLengthHints} = 0; + $options{showEndpointHints}=0; + $options{partialCredit}=0; + } else { + $ans_type = 'Interval'; + $options{showEndpointHints}=0; + } + } + $opts{tolType} = $opts{tolType} || 'relative'; + $opts{tolerance} = $opts{tolerance} || $opts{tol} || + $opts{reltol} || $opts{relTol} || $opts{abstol} || 1; + $opts{zeroLevel} = $opts{zeroLevel} || $opts{zeroLevelTol} || + $main::numZeroLevelTolDefault; + if ($opts{tolType} eq 'absolute' or defined($opts{tol}) + or defined($opts{abstol})) { + $context->flags->set( + tolerance => $opts{tolerance}, + tolType => 'absolute', + ); + } else { + $context->flags->set( + tolerance => .01*$opts{tolerance}, + tolType => 'relative', + ); + } + $context->flags->set( + zeroLevel => $opts{zeroLevel}, + zeroLevelTol => $opts{zeroLevelTol}, + ); + $options{ordered} = 1 if(defined($opts{ordered}) and $opts{ordered}); + if (defined($opts{'sloppy'}) && $opts{'sloppy'} eq 'yes') { + $options{requireParenMatch} = 0; + } + Context($context); + if($ans_type eq 'List') { + $ans_eval = List($correct_ans)->cmp(%options); + } elsif($ans_type eq 'Union') { + $ans_eval = Union($correct_ans)->cmp(%options); + warn "Union with options ".join(',', %options); + } elsif($ans_type eq 'Interval') { + $ans_eval = Interval($correct_ans)->cmp(%options); + } else { + warn "Bug -- should not be here"; + } + + Context($oldContext); + return($ans_eval); + + + # ToDo: tolerances + # modes? + # strings + # infinities + #@infinitywords = ("i", "inf", "infty", "infinity"); + #$infinityre = join '|', @infinitywords; + #$infinityre = "^([-+m]?)($infinityre)\$"; + + +} + sub interval_cmp { Interval_evaluator::interval_cmp(@_); } @@ -657,75 +749,76 @@ sub number_list_cmp { my $list = shift; - + my %num_params = @_; - - my $mode = $num_params{mode} || 'std'; - my %options = (debug => $num_params{debug}); - - # - # Get an apppropriate context based on the mode - # + + my $mode = $num_params{mode} || 'std'; + my %options = (debug => $num_params{debug}); + + # + # Get an apppropriate context based on the mode + # my $oldContext = Context(); - my $context; - #my $Context = sub {Parser::Context->current($user_context,@_)}; - for ($mode) { - /^strict$/i and do { - $context = Context("LimitedNumeric")->copy; - $context->operators->set(',' => {class=> 'Parser::BOP::comma'}); - last; - }; - /^arith$/i and do { - $context = Context("LegacyNumeric")->copy; - $context->functions->disable('All'); - last; - }; - /^frac$/i and do { - $context = Context("LimitedNumeric-Fraction")->copy; - $context->operators->set(',' => {class=> 'Parser::BOP::comma'}); - last; - }; - if(defined($num_params{'complex'}) && - ($num_params{'complex'} =~ /(yes|ok)/i)) { - $context = Context("Complex")->copy; - last; - } - - # default - $context = Context("LegacyNumeric")->copy; - } - $context->{format}{number} = $num_params{'format'} || $main::numFormatDefault; - $context->strings->clear; + my $context; + for ($mode) { + /^strict$/i and do { + $context = $Parser::Context::Default::context{LimitedNumeric}->copy; + $context->operators->set(',' => {class=> 'Parser::BOP::comma'}); + 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; + $context->operators->set(',' => {class=> 'Parser::BOP::comma'}); + last; + }; + if(defined($num_params{'complex'}) && + ($num_params{'complex'} =~ /(yes|ok)/i)) { + $context = $Parser::Context::Default::context{Complex}->copy; + last; + } + + # default + $context = $Parser::Context::Default::context{LegacyNumeric}->copy; + } + $context->{format}{number} = $num_params{'format'} || $main::numFormatDefault; + $context->strings->clear; if (defined($num_params{strings}) && $num_params{strings}) { - foreach my $string (@{$num_params{strings}}) { - my %tex = ($string =~ m/(-?)inf(inity)?/i)? (TeX => "$1\\infty"): (); - $context->strings->add(uc($string) => {%tex}); - } - } - + foreach my $string (@{$num_params{strings}}) { + my %tex = ($string =~ m/(-?)inf(inity)?/i)? (TeX => "$1\\infty"): (); + $context->strings->add(uc($string) => {%tex}); + } + } + $num_params{tolType} = $num_params{tolType} || 'relative'; - $num_params{tolerance} = $num_params{tolerance} || $num_params{tol} || $num_params{reltol} || $num_params{relTol} || $num_params{abstol} || 1; - $num_params{zeroLevel} = $num_params{zeroLevel} || $num_params{zeroLevelTol} || $main::numZeroLevelTolDefault; + $num_params{tolerance} = $num_params{tolerance} || $num_params{tol} || + $num_params{reltol} || $num_params{relTol} || $num_params{abstol} || 1; + $num_params{zeroLevel} = $num_params{zeroLevel} || $num_params{zeroLevelTol} || + $main::numZeroLevelTolDefault; if ($num_params{tolType} eq 'absolute' or defined($num_params{tol}) or defined($num_params{abstol})) { - $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}, - ); - $options{ordered} = 1 if(defined($num_params{ordered})); - + $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}, + ); + $options{ordered} = 1 if(defined($num_params{ordered}) and $opts{ordered}); + Context($context); - $ans_eval = List($list)->cmp(%options); + my $ans_eval = List($list)->cmp(%options); Context($oldContext); return($ans_eval); } |
From: Sam H. v. a. <we...@ma...> - 2005-08-10 18:00:47
|
Log Message: ----------- make .def searching thread-safe (resolves bug #814). changed @found_set_defs to be a lexical local to get_set_defs(). replaced get_set_defs_wanted() with a closure over @found_set_defs defined in get_set_defs(). output is identical to the previous case, but no global(-ish) variables are used. Modified Files: -------------- webwork2/lib/WeBWorK/ContentGenerator/Instructor: SetMaker.pm Revision Data ------------- Index: SetMaker.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm,v retrieving revision 1.48 retrieving revision 1.49 diff -Llib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm -Llib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm -u -r1.48 -r1.49 --- lib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm +++ lib/WeBWorK/ContentGenerator/Instructor/SetMaker.pm @@ -144,21 +144,18 @@ ## Search for set definition files -# initialize global variable for search -my @found_set_defs = (); - -sub get_set_defs_wanted { - my $fn = $_; - my $fdir = $File::Find::dir; - return() if($fn !~ /^set.*\.def$/); - #return() if(not -T $fn); - push @found_set_defs, "$fdir/$fn"; -} - sub get_set_defs { my $topdir = shift; - @found_set_defs = (); - find({ wanted => \&get_set_defs_wanted, follow_fast=>1}, $topdir); + my @found_set_defs; + # get_set_defs_wanted is a closure over @found_set_defs + my $get_set_defs_wanted = sub { + my $fn = $_; + my $fdir = $File::Find::dir; + return() if($fn !~ /^set.*\.def$/); + #return() if(not -T $fn); + push @found_set_defs, "$fdir/$fn"; + }; + find({ wanted => $get_set_defs_wanted, follow_fast=>1}, $topdir); map { $_ =~ s|^$topdir/?|| } @found_set_defs; return @found_set_defs; } |
From: dpvc v. a. <we...@ma...> - 2005-08-10 15:05:25
|
Log Message: ----------- Interval (and Union) checker now accepts requireParenMatch flag for deciding whether the interval type must match. Setting requireParenMatch to 0 will let (1,2) match (1,2] or [1,2], etc. Modified Files: -------------- pg/lib/Value: AnswerChecker.pm Interval.pm Revision Data ------------- Index: AnswerChecker.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/AnswerChecker.pm,v retrieving revision 1.49 retrieving revision 1.50 diff -Llib/Value/AnswerChecker.pm -Llib/Value/AnswerChecker.pm -u -r1.49 -r1.50 --- lib/Value/AnswerChecker.pm +++ lib/Value/AnswerChecker.pm @@ -760,6 +760,7 @@ shift->SUPER::cmp_defaults(@_), showEndpointHints => 1, showEndTypeHints => 1, + requireParenMatch => 1, )} sub typeMatch { @@ -772,6 +773,15 @@ $other->type =~ m/^(Interval|Union)$/; } +sub cmp_compare { + my $self = shift; my $other = shift; my $ans = shift; + my $oldignore = $self->{requireParenMatch}; + $self->{ignoreEndpointTypes} = !$ans->{requireParenMatch}; + my $equal = $self->SUPER::cmp_compare($other,$ans); + $self->{ignoreEndpointTypes} = $oldignore; + return $equal; +} + # # Check for wrong enpoints and wrong type of endpoints # @@ -788,7 +798,7 @@ push(@errors,"Your right endpoint is incorrect") if ($self->{data}[1] != $other->{data}[1]); } - if (scalar(@errors) == 0 && $ans->{showEndTypeHints}) { + if (scalar(@errors) == 0 && $ans->{showEndTypeHints} && $ans->{requireParenMatch}) { push(@errors,"The type of interval is incorrect") if ($self->{open}.$self->{close} ne $other->{open}.$other->{close}); } Index: Interval.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Interval.pm,v retrieving revision 1.19 retrieving revision 1.20 diff -Llib/Value/Interval.pm -Llib/Value/Interval.pm -u -r1.19 -r1.20 --- lib/Value/Interval.pm +++ lib/Value/Interval.pm @@ -174,8 +174,8 @@ if ($flag) {my $tmp = $l; $l = $r; $r = $tmp}; my ($la,$lb) = @{$l->data}; my ($ra,$rb) = @{$r->data}; my $cmp = $la <=> $ra; return $cmp if $cmp; + $cmp = $lb <=> $rb; return $cmp if $cmp || $l->{ignoreEndpointTypes}; $cmp = $l->{open} cmp $r->{open}; return $cmp if $cmp; - $cmp = $lb <=> $rb; return $cmp if $cmp; return $l->{close} cmp $r->{close}; } |
From: dpvc v. a. <we...@ma...> - 2005-08-10 15:03:47
|
Log Message: ----------- Cosmetic change. Modified Files: -------------- pg/lib: Value.pm Revision Data ------------- Index: Value.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value.pm,v retrieving revision 1.39 retrieving revision 1.40 diff -Llib/Value.pm -Llib/Value.pm -u -r1.39 -r1.40 --- lib/Value.pm +++ lib/Value.pm @@ -497,7 +497,7 @@ # sub Error { my $message = shift; - $message = [$message,@_] if (scalar(@_)); + $message = [$message,@_] if scalar(@_) > 0; $$context->setError($message,''); $message = $$context->{error}{message}; die $message . traceback() if $$context->{debug}; |
From: dpvc v. a. <we...@ma...> - 2005-08-10 14:27:18
|
Log Message: ----------- The Parser versions of num_cmp and fun_cmp incorrectly left the current Context set to the base context used by the answer checker. The Context is now correctly reset to the one in effect before the call to num_cmp or fun_cmp. Also removed some redundant values in if-then checks. 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.7 retrieving revision 1.8 diff -Llib/Parser/Legacy/PGanswermacros.pl -Llib/Parser/Legacy/PGanswermacros.pl -u -r1.7 -r1.8 --- lib/Parser/Legacy/PGanswermacros.pl +++ lib/Parser/Legacy/PGanswermacros.pl @@ -1046,21 +1046,21 @@ my $context; for ($mode) { /^strict$/i and do { - $context = &$Context("LimitedNumeric")->copy; + $context = $Parser::Context::Default::context{LimitedNumeric}->copy; last; }; /^arith$/i and do { - $context = &$Context("LegacyNumeric")->copy; + $context = $Parser::Context::Default::context{LegacyNumeric}->copy; $context->functions->disable('All'); last; }; /^frac$/i and do { - $context = &$Context("LimitedNumeric-Fraction")->copy; + $context = $Parser::Context::Default::context{'LimitedNumeric-Fraction'}->copy; last; }; # default - $context = &$Context("LegacyNumeric")->copy; + $context = $Parser::Context::Default::context{LegacyNumeric}->copy; } $context->{format}{number} = $num_params{'format'}; $context->strings->clear; @@ -1069,7 +1069,7 @@ # # Add the strings to the context # - if (defined($num_params{strings}) && $num_params{strings}) { + 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}); @@ -1100,7 +1100,7 @@ # using the initialized context # my $oldContext = &$Context($context); my $r; - if (defined($num_params{units}) && $num_params{units}) { + if ($num_params{units}) { $r = new Parser::Legacy::NumberWithUnits($correctAnswer); $options{rh_correct_units} = $num_params{units}; } else { |
From: dpvc v. a. <we...@ma...> - 2005-08-10 14:21:08
|
Log Message: ----------- Enable commas when mode=>"frac" is specified. Modified Files: -------------- pg/macros: extraAnswerEvaluators.pl Revision Data ------------- Index: extraAnswerEvaluators.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/extraAnswerEvaluators.pl,v retrieving revision 1.5 retrieving revision 1.6 diff -Lmacros/extraAnswerEvaluators.pl -Lmacros/extraAnswerEvaluators.pl -u -r1.5 -r1.6 --- macros/extraAnswerEvaluators.pl +++ macros/extraAnswerEvaluators.pl @@ -682,6 +682,7 @@ }; /^frac$/i and do { $context = Context("LimitedNumeric-Fraction")->copy; + $context->operators->set(',' => {class=> 'Parser::BOP::comma'}); last; }; if(defined($num_params{'complex'}) && |
From: Charlie & v. a. <we...@ma...> - 2005-08-09 22:21:00
|
Log Message: ----------- groundwork for preserving non-authentication state (which probably won't get used) Modified Files: -------------- webwork2/lib/WeBWorK: ContentGenerator.pm Revision Data ------------- Index: ContentGenerator.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator.pm,v retrieving revision 1.142 retrieving revision 1.143 diff -Llib/WeBWorK/ContentGenerator.pm -Llib/WeBWorK/ContentGenerator.pm -u -r1.142 -r1.143 --- lib/WeBWorK/ContentGenerator.pm +++ lib/WeBWorK/ContentGenerator.pm @@ -489,6 +489,22 @@ my $grades = $urlpath->newFromModule("${pfx}Grades", %args); my $logout = $urlpath->newFromModule("${pfx}Logout", %args); + # experimental subroutine for generating links, to clean up the rest of the + # code. ignore for now. (this is a closure over $self and $urlpath.) + #my $makelink = sub { + # my ($module, $args, $name) = @_; + # + # defined $args or $args = {}; + # my $new_urlpath = $urlpath->newFromModule($module, %$args); + # + # defined $name or $name = $new_urlpath->name; + # $name = sp2nbsp($name); # i don't like it, but that's what we do... + # + # return CGI::a({href => $self->systemLink($new_urlpath)}, $name); + #}; + # + #my $home_link = &$makelink("${pfx}Home"); + print "\n<!-- BEGIN " . __PACKAGE__ . "::links -->\n"; # only users with appropriate permissions can report bugs @@ -1422,6 +1438,23 @@ } } +=item hidden_state_fields() + +Use hidden_fields to return hidden <INPUT> tags for request fields used to +maintain state. Currently includes authentication fields and display option +fields. + +=cut + +sub hidden_state_fields { + my ($self) = @_; + + return $self->hidden_authen_fields(); + + # other things that may be state data: + #$self->hidden_fields("displayMode", "showOldAnswers", "showCorrectAnswers", "showHints", "showSolutions"); +} + =item url_args(@fields) Return a URL query string (without the leading `?') containing values for each @@ -1460,50 +1493,70 @@ return $self->url_args("user", "effectiveUser", "key"); } -=item url_display_args() +=item url_state_args() -Use url_args to return a URL query string for request fields used in -authentication. +Use url_args to return a URL query string for request fields used to maintain +state. Currently includes authentication fields and display option fields. =cut -sub url_display_args { +sub url_state_args { my ($self) = @_; - return $self->url_args("displayMode", "showOldAnswer"); -} - -=item print_form_data($begin, $middle, $end, $omit) - -Return a string containing every request field not matched by the quoted reguar -expression $omit, placing $begin before each field name, $middle between each -field name and its value, and $end after each value. Values are taken from the -current request. - -=cut - -sub print_form_data { - my ($self, $begin, $middle, $end, $qr_omit) = @_; - my $r=$self->r; - my @form_data = $r->param; + return $self->url_authen_args; - my $return_string = ""; - foreach my $name (@form_data) { - next if ($qr_omit and $name =~ /$qr_omit/); - my @values = $r->param($name); - foreach my $variable (qw(begin name middle value end)) { - # FIXME: can this loop be moved out of the enclosing loop? - no strict 'refs'; - ${$variable} = "" unless defined ${$variable}; - } - foreach my $value (@values) { - $return_string .= "$begin$name$middle$value$end"; - } - } - - return $return_string; + # other things that may be state data: + #$self->url_args("displayMode", "showOldAnswers", "showCorrectAnswers", "showHints", "showSolutions"); } +# This method is not used anywhere! --sam(1-Aug-05) +# +#=item url_display_args() +# +#Use url_args to return a URL query string for request fields used in +#authentication. +# +#=cut +# +#sub url_display_args { +# my ($self) = @_; +# +# return $self->url_args("displayMode", "showOldAnswer"); +#} + +# This method is not used anywhere! --sam(1-Aug-05) +# +#=item print_form_data($begin, $middle, $end, $omit) +# +#Return a string containing every request field not matched by the quoted reguar +#expression $omit, placing $begin before each field name, $middle between each +#field name and its value, and $end after each value. Values are taken from the +#current request. +# +#=cut +# +#sub print_form_data { +# my ($self, $begin, $middle, $end, $qr_omit) = @_; +# my $r=$self->r; +# my @form_data = $r->param; +# +# my $return_string = ""; +# foreach my $name (@form_data) { +# next if ($qr_omit and $name =~ /$qr_omit/); +# my @values = $r->param($name); +# foreach my $variable (qw(begin name middle value end)) { +# # FIXME: can this loop be moved out of the enclosing loop? +# no strict 'refs'; +# ${$variable} = "" unless defined ${$variable}; +# } +# foreach my $value (@values) { +# $return_string .= "$begin$name$middle$value$end"; +# } +# } +# +# return $return_string; +#} + =back =cut |
From: jj v. a. <we...@ma...> - 2005-08-08 15:54:23
|
Log Message: ----------- Changed number_list_cmp to use Parser. Modified Files: -------------- pg/macros: extraAnswerEvaluators.pl Revision Data ------------- Index: extraAnswerEvaluators.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/extraAnswerEvaluators.pl,v retrieving revision 1.4 retrieving revision 1.5 diff -Lmacros/extraAnswerEvaluators.pl -Lmacros/extraAnswerEvaluators.pl -u -r1.4 -r1.5 --- macros/extraAnswerEvaluators.pl +++ macros/extraAnswerEvaluators.pl @@ -1,4 +1,4 @@ - +loadMacros('Parser.pl'); # This is extraAnswerEvaluators.pl @@ -438,288 +438,6 @@ } - -{ - package Number_List; - - sub new { - my $class = shift; - my $base_string = shift; - my $self = {}; - $self->{'original'} = $base_string; - return bless $self, $class; - } - - sub make_complex_number { - my $instring = shift; - - $instring = main::math_constants($instring); - $instring =~ s/e\^/exp /g; - my $parser = new AlgParserWithImplicitExpand; - my $ret = $parser -> parse($instring); - $parser -> tostring(); - $parser -> normalize(); - $instring = $parser -> tostring(); - $instring =~ s/\bi\b/(i)/g; - my ($in,$PG_errors,$PG_errors_long) = main::PG_restricted_eval($instring); - return ($in+0*Complex1::i()); - } - - - - sub parse_number_list { - my($self) = shift; - my(%opts) = @_; - my($str) = $self->{'original'}; - my(@ans_list) = (); - my(@sort_list) = (); - delete($opts{'ordered'}); - - my $complex=0; - if(defined($opts{'complex'}) && - ($opts{'complex'} =~ /(yes|ok)/i)) { - $complex=1; - delete($opts{'mode'}); - } - delete($opts{'complex'}); - $self->{'normalized'} = ''; - $self->{'value'} = ''; - $self->{'latex'} = ''; - $self->{'htmlerror'} = ''; - $self->{'error_msg'} = ''; - my($cur) = ""; - my($level,$spot,$hold,$char) = (1,0,0,"a"); - my($strt, $end) = (0, length($str)); - my($specials) = '[\(\[\]\),\{\}]'; - my($tmp_ae,$tmp_ae2); - if($complex) { - $tmp_ae = main::cplx_cmp(new Complex(1,0), %opts); - $tmp_ae2 = main::cplx_cmp(new Complex(1,0)); - } else { - $tmp_ae = main::num_cmp(1, %opts); - $tmp_ae2 = main::num_cmp(1); - } - - while ($spot < $end) { - $char = substr($str,$spot,1); - if ($char=~ /$specials/) { # Its a special character - if ($char eq ",") { - if ($level == 1) { # Level 1 comma - $cur = substr($str,$hold, $spot-$hold); - my($tmp_ah); - $tmp_ah = $tmp_ae->evaluate($cur); - if(has_errors($tmp_ah)) { - $self->error("I could not parse your input correctly",[$hold, $spot]); - return 0; - } - $self->{'normalized'} .= (defined($tmp_ah->{'preview_text_string'}) ? $tmp_ah->{'preview_text_string'} : $tmp_ah->{'student_ans'}).", "; - $self->{'value'} .= $tmp_ah->{'student_ans'}.", "; - $self->{'latex'} .= (defined($tmp_ah->{'preview_latex_string'}) ? $tmp_ah->{'preview_latex_string'} : $tmp_ah->{'student_ans'}).", "; - $tmp_ah = $tmp_ae2->evaluate($cur); - $hold = $spot+1; - push @sort_list, [$cur,$tmp_ah->{'student_ans'}]; - push @ans_list, $cur; - } - } # end of comma - elsif ($char eq "[" or $char eq "(" or $char eq "{") { #opening - $level++; - } # end of open paren - else { # must be closing paren - if ($level == 1) { - $self->error("Not a valid entry; unmatched $char.",[$spot]); - return 0; - } # end of level <= 1 - $level--; - } # end of closing brace - } - $spot++; - } - - if($level>1) { - $self->error("Your expression has unmatched parens.", - [$hold, $spot]); - return 0; - } - $cur = substr($str,$hold, $spot-$hold); - - my($tmp_ah); - $tmp_ah = $tmp_ae->evaluate($cur); - - if(has_errors($tmp_ah)) { - $self->error("I could not parse your input correctly",[$hold, $spot]); - return 0; - } - if(not ($cur =~ /\w/)) { # Input was empty - $self->{'forsort'} = []; - return 1; - } - - $self->{'normalized'} .= defined($tmp_ah->{'preview_text_string'}) ? $tmp_ah->{'preview_text_string'} : $tmp_ah->{'student_ans'}; - $self->{'value'} .= $tmp_ah->{'student_ans'}; - $self->{'latex'} .= defined($tmp_ah->{'preview_latex_string'}) ? $tmp_ah->{'preview_latex_string'} : $tmp_ah->{'student_ans'}; - if((3==4) && $complex) { - $tmp_ah =&{$tmp_ae2}($cur); - } else { - $tmp_ah = $tmp_ae2->evaluate($cur); - } - $hold = $spot+1; - push @sort_list, [$cur, $tmp_ah->{'student_ans'}]; - push @ans_list, $cur; - - $self->{'parsed'} = \@ans_list; - $self->{'forsort'} = \@sort_list; - return 1; - } - - sub number_list_cmp { - my $right_ans = shift; - my %opts = @_; - - $opts{'mode'} = 'std' unless defined($opts{'mode'}); - $opts{'tolType'} = 'relative' unless defined($opts{'tolType'}); - - my $ans_eval = sub { - my $student = shift; - - my $ans_hash = new AnswerHash( - 'score'=>0, - 'correct_ans'=>$right_ans, - 'student_ans'=>$student, - 'original_student_ans' => $student, - # 'type' => undef, - 'ans_message'=>'', - 'preview_text_string'=>'', - 'preview_latex_string'=>'', - ); - my $student_list = new Number_List($student); - if(! $student_list->parse_number_list(%opts)) { - # Error in student input - $ans_hash->{'student_ans'} = "error: $student_list->{htmlerror}"; - $ans_hash->{'ans_message'} = "$student_list->{error_msg}"; - return $ans_hash; - } - - $ans_hash->{'student_ans'} = $student_list->{'value'}; - $ans_hash->{'preview_text_string'} = $student_list->{'normalized'}; - $ans_hash->{'preview_latex_string'} = $student_list->{'latex'}; - - my $correct_list = new Number_List($right_ans); - if(! $correct_list->parse_number_list(%opts)) { - # Cannot parse instuctor's answer! - $ans_hash->{'ans_message'} = "Tell your professor that there is an error in this problem."; - return $ans_hash; - } - if (cmp_numlists($correct_list, $student_list, %opts)) { - $ans_hash -> setKeys('score' => 1); - } - - return $ans_hash; - }; - - return $ans_eval; - } - - sub sorting_sub { - $_[0]->[1] <=> $_[1]->[1]; - } - - sub cmp_numlists { - my($in1) = shift; - my($in2) = shift; - my(%opts) = @_; - my($strict_ordering) = 0; - if (defined($opts{'ordered'}) && ($opts{'ordered'} eq 'yes')) { - $strict_ordering = 1; - } - delete($opts{'ordered'}); - - my $complex=0; - if(defined($opts{'complex'}) && - ($opts{'complex'} =~ /(yes|ok)/i)) { - $complex=1; - delete($opts{'mode'}); - } - delete($opts{'complex'}); - - my(@fs1) = @{$in1->{'forsort'}}; - my(@fs2) = @{$in2->{'forsort'}}; - - - # Same number of values? - if (scalar(@fs1) != scalar(@fs2)) { - return 0; - } - - my($j); - if($complex) { - for $j (@fs1) {$j->[1] = make_complex_number($j->[1]);} - for $j (@fs2) {$j->[1] = make_complex_number($j->[1]);} - } - - if($strict_ordering==0) { - @fs1 = main::PGsort(sub {$_[0]->[1] <=$_[1]->[1];}, @fs1); - @fs2 = main::PGsort(sub {$_[0]->[1] < $_[1]->[1];}, @fs2); - } - - for ($j=0; $j<scalar(@fs1);$j++) { - my $ae; - if($complex) { - $ae = main::cplx_cmp($fs1[$j]->[1], %opts); - } else { - $ae = main::num_cmp($fs1[$j]->[0], %opts); - } - my $result; - if($complex) { - $result =$ae->evaluate($fs2[$j]->[1]); - } else { - $result = $ae->evaluate($fs2[$j]->[0]); - } - if ($result->{score} == 0) { - return 0; - } - } - return 1; - } - - # error routine copied from AlgParser - sub error { - my($self, @args) = @_; - # we cheat to use error from algparser - my($ap) = new AlgParser(); - $ap->inittokenizer($self->{'original'}); - $ap->error(@args); - $self->{htmlerror} = $ap->{htmlerror}; - $self->{error_msg} = $ap->{error_msg}; - } - - sub has_errors { - my($ah) = shift; - - if($ah->{'student_ans'} =~ /error/) { - return 1; - } - my($am) = $ah->{'ans_message'}; - if($am =~ /error/) { - return 2; - } - if($am =~ /must enter/) { - return 3; - } - if($am =~ /does not evaluate/) { - return 4; - } - return 0; - } - -# Syntax is -# interval_cmp("[1,2) U [3, infty)", options) -# where options are key/value pairs for num_cmp. Also, we allow the option -# 'ordering' which can be 'strict', which means that we do not want to test rearrangements -# of the intervals. - - -} - { package Equation_eval; @@ -933,14 +651,85 @@ number_list_cmp("none", strings=>['none']) -will makr "none" as correct. +will mark "none" as correct. =cut sub number_list_cmp { - Number_List::number_list_cmp(@_); + my $list = shift; + + my %num_params = @_; + + my $mode = $num_params{mode} || 'std'; + my %options = (debug => $num_params{debug}); + + # + # Get an apppropriate context based on the mode + # + my $oldContext = Context(); + my $context; + #my $Context = sub {Parser::Context->current($user_context,@_)}; + for ($mode) { + /^strict$/i and do { + $context = Context("LimitedNumeric")->copy; + $context->operators->set(',' => {class=> 'Parser::BOP::comma'}); + last; + }; + /^arith$/i and do { + $context = Context("LegacyNumeric")->copy; + $context->functions->disable('All'); + last; + }; + /^frac$/i and do { + $context = Context("LimitedNumeric-Fraction")->copy; + last; + }; + if(defined($num_params{'complex'}) && + ($num_params{'complex'} =~ /(yes|ok)/i)) { + $context = Context("Complex")->copy; + last; + } + + # default + $context = Context("LegacyNumeric")->copy; + } + $context->{format}{number} = $num_params{'format'} || $main::numFormatDefault; + $context->strings->clear; + if (defined($num_params{strings}) && $num_params{strings}) { + foreach my $string (@{$num_params{strings}}) { + my %tex = ($string =~ m/(-?)inf(inity)?/i)? (TeX => "$1\\infty"): (); + $context->strings->add(uc($string) => {%tex}); + } + } + + $num_params{tolType} = $num_params{tolType} || 'relative'; + $num_params{tolerance} = $num_params{tolerance} || $num_params{tol} || $num_params{reltol} || $num_params{relTol} || $num_params{abstol} || 1; + $num_params{zeroLevel} = $num_params{zeroLevel} || $num_params{zeroLevelTol} || $main::numZeroLevelTolDefault; + if ($num_params{tolType} eq 'absolute' or defined($num_params{tol}) + or defined($num_params{abstol})) { + $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}, + ); + $options{ordered} = 1 if(defined($num_params{ordered})); + + Context($context); + $ans_eval = List($list)->cmp(%options); + Context($oldContext); + return($ans_eval); } + =head3 equation_cmp () Compares an equation. This really piggy-backs off of fun_cmp. It looks |