

1  ################################################################################ 


2  # WeBWorK Online Homework Delivery System 


3  # Copyright © 20002007 The WeBWorK Project, http://openwebwork.sf.net/ 


4  # $CVSHeader: pg/macros/parserFormulaUpToConstant.pl,v 1.14 2008/09/12 21:31:43 dpvc Exp $ 


5  # 


6  # This program is free software; you can redistribute it and/or modify it under 


7  # the terms of either: (a) the GNU General Public License as published by the 


8  # Free Software Foundation; either version 2, or (at your option) any later 


9  # version, or (b) the "Artistic License" which comes with this package. 


10  # 


11  # This program is distributed in the hope that it will be useful, but WITHOUT 


12  # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 


13  # FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the 


14  # Artistic License for more details. 


15  ################################################################################ 


16  


17  =head1 NAME 


18  


19  parserFormulaUpToConstant.pl  implements formulas "plus a constant". 


20  


21  =head1 DESCRIPTION 


22  


23  This file implements the FormulaUpToConstant object, which is 


24  a formula that is only unique up to a constant (i.e., this is 


25  an antiderivative). Students must include the "+C" as part of 


26  their answers, but they can use any (singleletter) constant that 


27  they want, and it doesn't have to be the one the professor used. 


28  


29  To use FormulaWithConstat objects, load this macro file at the 


30  top of your problem: 


31  


32  loadMacros("parserFormulaUpToConstant.pl"); 


33  


34  then create a formula with constant as follows: 


35  


36  $f = FormulaUpToConstant("sin(x)+C"); 


37  


38  Note that the C should NOT already be a variable in the Context; 


39  the FormulaUpToConstant object will handle adding it in for 


40  you. If you don't include a constant in your formula (i.e., if 


41  all the variables that you used are already in your Context, 


42  then the FormulaUpToConstant object will add "+C" for you. 


43  


44  The FormulaUpToConstant should work like any normal Formula, 


45  and in particular, you use $f>cmp to get its answer checker. 


46  


47  ANS($f>cmp); 


48  


49  Note that the FormulaUpToConstant object creates its only private 


50  copy of the current Context (so that it can add variables without 


51  affecting the rest of the problem). You should not notice this 


52  in general, but if you need to access that context, use $f>{context}. 


53  E.g. 


54  


55  Context($f>{context}); 


56  


57  would make the current context the one being used by the 


58  FormulaUpToConstant, while 


59  


60  $f>{context}>variables>names 


61  


62  would return a list of the variables in the private context. 


63  


64  To get the name of the constant in use in the formula, 


65  use 


66  


67  $f>constant. 


68  


69  If you combine a FormulaUpToConstant with other formulas, 


70  the result will be a new FormulaUpToConstant object, with 


71  a new Context, and potentially a new + C added to it. This 


72  is likely not what you want. Instead, you should convert 


73  back to a Formula first, then combine with other objects, 


74  then convert back to a FormulaUpToConstant, if necessary. 


75  To do this, use the removeConstant() method: 


76  


77  $f = FormulaUpToConstant("sin(x)+C"); 


78  $g = Formula("cos(x)"); 


79  $h = $f>removeConstant + $g; # $h will be "sin(x)+cos(x)" 


80  $h = FormulaUpToConstant($h); # $h will be "sin(x)+cos(x)+C" 


81  


82  The answer evaluator by default will give "helpful" messages 


83  to the student when the "+ C" is left out. You can turn off 


84  these messages using the showHints option to the cmp() method: 


85  


86  ANS($f>cmp(showHints => 0)); 


87  


88  One of the hints is about whether the student's answer is linear 


89  in the arbitrary constant. This test requires differentiating 


90  the student answer. Since there are times when that could be 


91  problematic, you can disable that test via the showLinearityHints 


92  flag. (Note: setting showHints to 0 also disables these hints.) 


93  


94  ANS($f>cmp(showLinearityHints => 0)); 


95  


96  =cut 


97  
1  loadMacros("MathObjects.pl"); 
98  loadMacros("MathObjects.pl"); 
2  
99  
3  sub _parserFormulaUpToConstant_init {FormulaUpToConstant::Init()} 
100  sub _parserFormulaUpToConstant_init {FormulaUpToConstant::Init()} 
4  


5  =head1 FormulaUpToConstant(); 


6  


7  ###################################################################### 


8  # 


9  # This file implements the FormulaUpToConstant object, which is 


10  # a formula that is only unique up to a constant (i.e., this is 


11  # an antiderivative). Students must include the "+C" as part of 


12  # their answers, but they can use any (singleletter) constant that 


13  # they want, and it doesn't have to be the one the professor used. 


14  # 


15  # To use FormulaWithConstat objects, load this macro file at the 


16  # top of your problem: 


17  # 


18  # loadMacros("parserFormulaUpToConstant.pl"); 


19  # 


20  # then create a formula with constant as follows: 


21  # 


22  # $f = FormulaUpToConstant("sin(x)+C"); 


23  # 


24  # Note that the C should NOT already be a variable in the Context; 


25  # the FormulaUpToConstant object will handle adding it in for 


26  # you. If you don't include a constant in your formula (i.e., if 


27  # all the variables that you used are already in your Context, 


28  # then the FormulaUpToConstant object will add "+C" for you. 


29  # 


30  # The FormulaUpToConstant should work like any normal Formula, 


31  # and in particular, you use $f>cmp to get its answer checker. 


32  # 


33  # ANS($f>cmp); 


34  # 


35  # Note that the FormulaUpToConstant object creates its only private 


36  # copy of the current Context (so that it can add variables without 


37  # affecting the rest of the problem). You should not notice this 


38  # in general, but if you need to access that context, use $f>{context}. 


39  # E.g. 


40  # 


41  # Context($f>{context}); 


42  # 


43  # would make the current context the one being used by the 


44  # FormulaUpToConstant, while 


45  # 


46  # $f>{context}>variables>names 


47  # 


48  # would return a list of the variables in the private context. 


49  # 


50  # To get the name of the constant in use in the formula, 


51  # use 


52  # 


53  # $f>constant. 


54  # 


55  # If you combine a FormulaUpToConstant with other formulas, 


56  # the result will be a new FormulaUpToConstant object, with 


57  # a new Context, and potentially a new + C added to it. This 


58  # is likely not what you want. Instead, you should convert 


59  # back to a Formula first, then combine with other objects, 


60  # then convert back to a FormulaUpToConstant, if necessary. 


61  # To do this, use the removeConstant() method: 


62  # 


63  # $f = FormulaUpToConstant("sin(x)+C"); 


64  # $g = Formula("cos(x)"); 


65  # $h = $f>removeConstant + $g; # $h will be "sin(x)+cos(x)" 


66  # $h = FormulaUpToConstant($h); # $h will be "sin(x)+cos(x)+C" 


67  # 


68  # The answer evaluator by default will give "helpful" messages 


69  # to the student when the "+ C" is left out. You can turn off 


70  # these messages using the showHints option to the cmp() method: 


71  # 


72  # ANS($f>cmp(showHints => 0)); 


73  # 


74  # One of the hints is about whether the student's answer is linear 


75  # in the arbitrary constant. This test requires differentiating 


76  # the student answer. Since there are times when that could be 


77  # problematic, you can disable that test via the showLinearityHints 


78  # flag. (Note: setting showHints to 0 also disables these hints.) 


79  # 


80  # ANS($f>cmp(showLinearityHints => 0)); 


81  # 


82  ###################################################################### 


83  


84  =cut 


85  
101  
86  package FormulaUpToConstant; 
102  package FormulaUpToConstant; 
87  @ISA = ('Value::Formula'); 
103  @ISA = ('Value::Formula'); 
88  
104  
89  sub Init { 
105  sub Init { 
…  
…  
120  # 
136  # 
121  my $n = $f>D($f>{constant}); 
137  my $n = $f>D($f>{constant}); 
122  Value>Error("Your formula isn't linear in the arbitrary constant '%s'",$f>{constant}) 
138  Value>Error("Your formula isn't linear in the arbitrary constant '%s'",$f>{constant}) 
123  unless $n>isConstant; 
139  unless $n>isConstant; 
124  # 
140  # 
125  # Make a version with an adaptive parameter for use in the 
141  # Make a version with adaptive parameters for use in the 
126  # comparison later on. We could like n0*C, but already have $n 
142  # comparison later on. We could like n0*C, but already have $n 
127  # copies of C, so remove them. That way, n0 will be 0 when there 
143  # copies of C, so remove them. That way, n0 will be 0 when there 
128  # are no C's in the student answer during the adaptive comparison. 
144  # are no C's in the student answer during the adaptive comparison. 
129  # (Again, should really check that n0 is not in use already) 
145  # (Again, should really check that n0 is not in use already) 
130  # 
146  # 
131  my $n0 = $context>variables>get("n0"); 
147  my $n00 = $context>variables>get("n00"); 
132  $context>variables>add(n0=>'Parameter') unless $n0 and $n0>{parameter}; 
148  $context>variables>add(n00=>'Parameter') unless $n00 and $n00>{parameter}; 


149  my $n01 = $context>variables>get("n01"); 


150  $context>variables>add(n01=>'Parameter') unless $n01 and $n01>{parameter}; 
133  $f>{adapt} = $f + "(n0$n)$f>{constant}"; 
151  $f>{adapt} = $f + "(n00$n)$f>{constant} + n01"; 


152  
134  return bless $f, $class; 
153  return bless $f, $class; 
135  } 
154  } 
136  
155  
137  ################################################## 
156  ################################################## 
138  # 
157  # 
…  
…  
155  # 
174  # 
156  $r = $r>substitute($r>{constant}=>$l>{constant}) unless $r>{constant} eq $l>{constant}; 
175  $r = $r>substitute($r>{constant}=>$l>{constant}) unless $r>{constant} eq $l>{constant}; 
157  # 
176  # 
158  # Compare with adaptive parameters to see if $l + n0 C = $r for some n0. 
177  # Compare with adaptive parameters to see if $l + n0 C = $r for some n0. 
159  # 
178  # 
160  return 1 unless $l>{adapt} == $r; 
179  $main::{_cmp_} = sub {return ($l>{adapt}>inherit($l)) == $r}; # a closure to access local variables 


180  my $equal = main::PG_restricted_eval('&{$main::{_cmp_}}'); # prevents errors with large adaptive parameters 


181  delete $main::{_cmp_}; # remove temprary function 


182  return 1 unless $equal; 
161  # 
183  # 
162  # Check that n0 is nonzero (i.e., there is a multiple of C in the student answer) 
184  # Check that n0 is nonzero (i.e., there is a multiple of C in the student answer) 
163  # (remember: return value of 0 is equal, and nonzero is unequal) 
185  # (remember: return value of 0 is equal, and nonzero is unequal) 
164  # 
186  # 
165  return abs($context>variables>get("n0")>{value}) < $context>flag("zeroLevelTol"); 
187  return abs($context>variables>get("n00")>{value}) < $context>flag("zeroLevelTol"); 
166  } 
188  } 
167  
189  
168  ################################################## 
190  ################################################## 
169  # 
191  # 
170  # Here we override part of the answer comparison 
192  # Here we override part of the answer comparison 
…  
…  
187  return unless $ans>{score} == 0 && !$ans>{isPreview}; 
209  return unless $ans>{score} == 0 && !$ans>{isPreview}; 
188  return if $ans>{ans_message}  !$self>getFlag("showHints"); 
210  return if $ans>{ans_message}  !$self>getFlag("showHints"); 
189  my $student = $ans>{student_value}; 
211  my $student = $ans>{student_value}; 
190  my $result = $ans>{correct_value} <=> $student; # compare encodes the reason in the result 
212  my $result = $ans>{correct_value} <=> $student; # compare encodes the reason in the result 
191  $self>cmp_Error($ans,"Note: there is always more than one posibility") if $result == 2  $result == 3; 
213  $self>cmp_Error($ans,"Note: there is always more than one posibility") if $result == 2  $result == 3; 


214  if ($result == 3) { 


215  my $context = $self>context; 


216  $context>flags>set(no_parameters=>0); 


217  $context>variables>add(x00=>'Real'); 


218  my $correct = $self>removeConstant+"n01+n00x00"; # must use both parameters 


219  $main::{_cmp_} = sub {return $correct == $student+"x00"}; # a closure to access local variables 


220  $result = 1 if main::PG_restricted_eval('&{$main::{_cmp_}}'); # prevents domain errors (and other errors) 


221  delete $main::{_cmp_}; # remove temprary function 


222  $context>variables>remove('x00'); 


223  $context>flags>set(no_parameters=>1); 


224  } 
192  $self>cmp_Error($ans,"Your answer is not the most general solution") 
225  $self>cmp_Error($ans,"Your answer is not the most general solution") if $result == 1; 
193  if $result == 1  ($result == 3 && $self>removeConstant == $student); 


194  $self>cmp_Error($ans,"Your formula should be linear in the constant '$student>{constant}'") 
226  $self>cmp_Error($ans,"Your formula should be linear in the constant '$student>{constant}'") 
195  if $result == 1 && $self>getFlag("showLinearityHints") && !$student>D($student>{constant})>isConstant; 
227  if $result == 1 && $self>getFlag("showLinearityHints") && !$student>D($student>{constant})>isConstant; 
196  } 
228  } 
197  
229  
198  ################################################## 
230  ################################################## 
…  
…  
204  # 
236  # 
205  # Remove the constant and return a Formula object 
237  # Remove the constant and return a Formula object 
206  # 
238  # 
207  sub removeConstant { 
239  sub removeConstant { 
208  my $self = shift; 
240  my $self = shift; 
209  main::Formula($self>substitute($self>{constant}=>0))>reduce; 
241  main::Formula($self>substitute($self>{constant}=>0))>reduce>inherit($self); 
210  } 
242  } 
211  
243  
212  # 
244  # 
213  # Override the differentiation so that we always return 
245  # Override the differentiation so that we always return 
214  # a Formula, not a FormulaUpToConstant (we don't want to 
246  # a Formula, not a FormulaUpToConstant (we don't want to 