[system] / trunk / pg / macros / contextLimitedPolynomial.pl Repository:
ViewVC logotype

Annotation of /trunk/pg/macros/contextLimitedPolynomial.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 4121 - (view) (download) (as text)

1 : dpvc 3294 loadMacros("Parser.pl");
2 :    
3 :     sub _contextLimitedPolynomial_init {}; # don't load it again
4 :    
5 :     ##########################################################
6 :     #
7 :     # Implements a context in which students can only
8 :     # enter (expanded) polynomials (i.e., sums of multiples
9 :     # of powers of x).
10 :     #
11 :     # Select the context using:
12 :     #
13 :     # Context("LimitedPolynomial");
14 :     #
15 :     # If you set the "singlePowers" flag, then only one monomial of
16 :     # each degree can be included in the polynomial:
17 :     #
18 :     # Context("LimitedPolynomial")->flags->set(singlePowers=>1);
19 :     #
20 :    
21 :     #
22 :     # Handle common checking for BOPs
23 :     #
24 :     package LimitedPolynomial::BOP;
25 :    
26 :     #
27 :     # Do original check and then if the operands are numbers, its OK.
28 :     # Otherwise, do an operator-specific check for if the polynomial is OK.
29 :     # Otherwise report an error.
30 :     #
31 :     sub _check {
32 :     my $self = shift;
33 :     my $super = ref($self); $super =~ s/LimitedPolynomial/Parser/;
34 :     &{$super."::_check"}($self);
35 :     return if LimitedPolynomial::isConstant($self->{lop}) &&
36 :     LimitedPolynomial::isConstant($self->{rop});
37 :     return if $self->checkPolynomial;
38 :     $self->Error("Your answer doesn't look like a polynomial");
39 :     }
40 :    
41 :     #
42 :     # filled in by subclasses
43 :     #
44 :     sub checkPolynomial {return 0}
45 :    
46 :     #
47 :     # Check that the powers of combined monomials are OK
48 :     # and record the new power list
49 :     #
50 :     sub checkPowers {
51 :     my $self = shift;
52 :     my ($l,$r) = ($self->{lop},$self->{rop});
53 :     my $single = $self->{equation}{context}->flag('singlePowers');
54 : dpvc 4121 $l->{powers} = {1=>1} if $l->class eq 'Variable';
55 :     $r->{powers} = {1=>1} if $r->class eq 'Variable';
56 : dpvc 3294 $self->{isPoly} = 1;
57 :     $self->{powers} = $l->{powers}? {%{$l->{powers}}} : {};
58 :     return 1 unless $r->{powers};
59 :     foreach my $n (keys(%{$r->{powers}})) {
60 :     $self->Error("Polynomials can have at most one term of each degree")
61 :     if $self->{powers}{$n} && $single;
62 :     $self->{powers}{$n} = 1;
63 :     }
64 :     return 1;
65 :     }
66 :    
67 :     package LimitedPolynomial;
68 :    
69 :     #
70 :     # Check for a constant expression
71 :     #
72 :     sub isConstant {
73 :     my $self = shift;
74 :     return 1 if $self->{isConstant} || $self->class eq 'Constant';
75 :     return scalar(keys(%{$self->getVariables})) == 0;
76 :     }
77 :    
78 :     ##############################################
79 :     #
80 :     # Now we get the individual replacements for the operators
81 :     # that we don't want to allow. We inherit everything from
82 :     # the original Parser::BOP class, and just add the
83 :     # polynomial checks here. Note that checkpolynomial
84 :     # only gets called if at least one of the terms is not
85 :     # a number.
86 :     #
87 :    
88 :     package LimitedPolynomial::BOP::add;
89 :     our @ISA = qw(LimitedPolynomial::BOP Parser::BOP::add);
90 :    
91 :     sub checkPolynomial {
92 :     my $self = shift;
93 :     my ($l,$r) = ($self->{lop},$self->{rop});
94 :     $self->Error("Addition is allowed only between monomials")
95 :     if $r->{isPoly};
96 :     $self->checkPowers;
97 :     }
98 :    
99 :     ##############################################
100 :    
101 :     package LimitedPolynomial::BOP::subtract;
102 :     our @ISA = qw(LimitedPolynomial::BOP Parser::BOP::subtract);
103 :    
104 :     sub checkPolynomial {
105 :     my $self = shift;
106 :     my ($l,$r) = ($self->{lop},$self->{rop});
107 :     $self->Error("Subtraction is only allowed between monomials")
108 :     if $r->{isPoly};
109 :     $self->checkPowers;
110 :     }
111 :    
112 :     ##############################################
113 :    
114 :     package LimitedPolynomial::BOP::multiply;
115 :     our @ISA = qw(LimitedPolynomial::BOP Parser::BOP::multiply);
116 :    
117 :     sub checkPolynomial {
118 :     my $self = shift;
119 :     my ($l,$r) = ($self->{lop},$self->{rop});
120 :     if (LimitedPolynomial::isConstant($l) && ($r->{isPower} || $r->class eq 'Variable')) {
121 :     $r->{powers} = {1=>1} unless $r->{isPower};
122 :     $self->{powers} = {%{$r->{powers}}};
123 :     return 1;
124 :     }
125 :     $self->Error("Coefficients must come before variables in a polynomial")
126 :     if LimitedPolynomial::isConstant($r) && ($l->{isPower} || $l->class eq 'Variable');
127 :     $self->Error("Multiplication can only be used between coefficients and variables");
128 :     }
129 :    
130 :     ##############################################
131 :    
132 :     package LimitedPolynomial::BOP::divide;
133 :     our @ISA = qw(LimitedPolynomial::BOP Parser::BOP::divide);
134 :    
135 :     sub checkPolynomial {
136 :     my $self = shift;
137 :     my ($l,$r) = ($self->{lop},$self->{rop});
138 :     $self->Error("You can only divide by a number in a polynomial")
139 :     unless LimitedPolynomial::isConstant($r);
140 :     $self->Error("You can only divide a single monomial by a number")
141 :     if $l->{isPoly} && $l->{isPoly} == 1;
142 :     $self->{isPoly} = $l->{isPoly};
143 :     $self->{powers} = {%{$l->{powers}}} if $l->{powers};
144 :     return 1;
145 :     }
146 :    
147 :     ##############################################
148 :    
149 :     package LimitedPolynomial::BOP::power;
150 :     our @ISA = qw(LimitedPolynomial::BOP Parser::BOP::power);
151 :    
152 :     sub checkPolynomial {
153 :     my $self = shift;
154 :     my ($l,$r) = ($self->{lop},$self->{rop});
155 :     $self->{isPower} = 1;
156 :     $self->Error("You can only raise a variable to a power in a polynomial")
157 :     unless $l->class eq 'Variable';
158 :     $self->Error("Exponents must be constant in a polynomial")
159 :     unless LimitedPolynomial::isConstant($r);
160 :     my $n = Parser::Evaluate($r);
161 :     $r->Error($$Value::context->{error}{message}) if $$Value::context->{error}{flag};
162 :     $self->Error("Exponents must be positive integers in a polynomial")
163 :     unless $n > 0 && $n == int($n);
164 :     $self->{powers} = {$n=>1};
165 :     return 1;
166 :     }
167 :    
168 :     ##############################################
169 :     ##############################################
170 :     #
171 :     # Now we do the same for the unary operators
172 :     #
173 :    
174 :     package LimitedPolynomial::UOP;
175 :    
176 :     sub _check {
177 :     my $self = shift;
178 :     my $super = ref($self); $super =~ s/LimitedPolynomial/Parser/;
179 :     &{$super."::_check"}($self);
180 :     my $op = $self->{op};
181 : jj 3383 return if LimitedPolynomial::isConstant($op);
182 : dpvc 3371 $self->Error("You can only use '%s' with monomials",$self->{def}{string})
183 : dpvc 3294 if $op->{isPoly};
184 :     $self->{isPoly} = 2;
185 :     $self->{powers} = {%{$op->{powers}}} if $op->{powers};
186 :     }
187 :    
188 :     sub checkPolynomial {return 0}
189 :    
190 :     ##############################################
191 :    
192 :     package LimitedPolynomial::UOP::plus;
193 :     our @ISA = qw(LimitedPolynomial::UOP Parser::UOP::plus);
194 :    
195 :     ##############################################
196 :    
197 :     package LimitedPolynomial::UOP::minus;
198 :     our @ISA = qw(LimitedPolynomial::UOP Parser::UOP::minus);
199 :    
200 :     ##############################################
201 :     ##############################################
202 :     #
203 :     # Don't allow absolute values
204 :     #
205 :    
206 :     package LimitedPolynomial::List::AbsoluteValue;
207 :     our @ISA = qw(Parser::List::AbsoluteValue);
208 :    
209 :     sub _check {
210 :     my $self = shift;
211 :     $self->SUPER::_check;
212 :     return if LimitedPolynomial::isConstant($self->{coords}[0]);
213 :     $self->Error("Can't use absolute values in polynomials");
214 :     }
215 :    
216 :     ##############################################
217 :     ##############################################
218 :     #
219 :     # Only allow numeric function calls
220 :     #
221 :    
222 :     package LimitedPolynomial::Function;
223 :    
224 :     sub _check {
225 :     my $self = shift;
226 :     my $super = ref($self); $super =~ s/LimitedPolynomial/Parser/;
227 :     &{$super."::_check"}($self);
228 :     my $arg = $self->{params}->[0];
229 :     return if LimitedPolynomial::isConstant($arg);
230 : dpvc 3371 $self->Error("Function '%s' can only be used with numbers",$self->{name});
231 : dpvc 3294 }
232 :    
233 :    
234 :     package LimitedPolynomial::Function::numeric;
235 :     our @ISA = qw(LimitedPolynomial::Function Parser::Function::numeric);
236 :    
237 :     package LimitedPolynomial::Function::trig;
238 :     our @ISA = qw(LimitedPolynomial::Function Parser::Function::trig);
239 :    
240 :     ##############################################
241 :     ##############################################
242 :    
243 :     package main;
244 :    
245 :     #
246 :     # Now build the new context that calls the
247 :     # above classes rather than the usual ones
248 :     #
249 :    
250 :     $context{LimitedPolynomial} = Context("Numeric");
251 :     $context{LimitedPolynomial}->operators->set(
252 :     '+' => {class => 'LimitedPolynomial::BOP::add'},
253 :     '-' => {class => 'LimitedPolynomial::BOP::subtract'},
254 :     '*' => {class => 'LimitedPolynomial::BOP::multiply'},
255 :     '* ' => {class => 'LimitedPolynomial::BOP::multiply'},
256 :     ' *' => {class => 'LimitedPolynomial::BOP::multiply'},
257 :     ' ' => {class => 'LimitedPolynomial::BOP::multiply'},
258 :     '/' => {class => 'LimitedPolynomial::BOP::divide'},
259 :     ' /' => {class => 'LimitedPolynomial::BOP::divide'},
260 :     '/ ' => {class => 'LimitedPolynomial::BOP::divide'},
261 :     '^' => {class => 'LimitedPolynomial::BOP::power'},
262 :     '**' => {class => 'LimitedPolynomial::BOP::power'},
263 :     'u+' => {class => 'LimitedPolynomial::UOP::plus'},
264 :     'u-' => {class => 'LimitedPolynomial::UOP::minus'},
265 :     );
266 :     #
267 :     # Remove these operators and functions
268 :     #
269 :     $context{LimitedPolynomial}->lists->set(
270 :     AbsoluteValue => {class => 'LimitedPolynomial::List::AbsoluteValue'},
271 :     );
272 :     $context{LimitedPolynomial}->operators->undefine('_','!','U');
273 :     $context{LimitedPolynomial}->functions->disable("Hyperbolic","atan2");
274 :     #
275 :     # Hook into the numeric and trig functions
276 :     #
277 :     foreach ('sin','cos','tan','sec','csc','cot',
278 :     'asin','acos','atan','asec','acsc','acot') {
279 :     $context{LimitedPolynomial}->functions->set(
280 :     "$_"=>{class => 'LimitedPolynomial::Function::trig'}
281 :     );
282 :     }
283 :     foreach ('ln','log','log10','exp','sqrt','abs','int','sgn') {
284 :     $context{LimitedPolynomial}->functions->set(
285 :     "$_"=>{class => 'LimitedPolynomial::Function::numeric'}
286 :     );
287 :     }
288 :    
289 :     Context("LimitedPolynomial");

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9