| … | |
… | |
| 5 | |
5 | |
| 6 | =head3 Context("LimitedPolynomial") |
6 | =head3 Context("LimitedPolynomial") |
| 7 | |
7 | |
| 8 | ########################################################## |
8 | ########################################################## |
| 9 | # |
9 | # |
| 10 | # Implements a context in which students can only |
10 | # Implements a context in which students can only enter (expanded) |
| 11 | # enter (expanded) polynomials (i.e., sums of multiples |
11 | # polynomials (i.e., sums of multiples of powers of x). |
| 12 | # of powers of x). |
|
|
| 13 | # |
12 | # |
| 14 | # Select the context using: |
13 | # Select the context using: |
| 15 | # |
14 | # |
| 16 | # Context("LimitedPolynomial"); |
15 | # Context("LimitedPolynomial"); |
| 17 | # |
16 | # |
| 18 | # If you set the "singlePowers" flag, then only one monomial of |
17 | # If you set the "singlePowers" flag, then only one monomial of each |
| 19 | # each degree can be included in the polynomial: |
18 | # degree can be included in the polynomial: |
| 20 | # |
19 | # |
| 21 | # Context("LimitedPolynomial")->flags->set(singlePowers=>1); |
20 | # Context("LimitedPolynomial")->flags->set(singlePowers=>1); |
|
|
21 | # |
|
|
22 | # There is also a strict limited context that does not allow |
|
|
23 | # operations even within the coefficients. Select it using: |
|
|
24 | # |
|
|
25 | # Context("LimitedPolynomial-Strict"); |
|
|
26 | # |
|
|
27 | # In addition to disallowing operations within the coefficients, |
|
|
28 | # this context does not reduce constant operations (since they are |
|
|
29 | # not allowed), and sets the singlePowers flag automatically. In |
|
|
30 | # addition, it disables all the functions, though they can be |
|
|
31 | # re-enabled, if needed. |
| 22 | # |
32 | # |
| 23 | |
33 | |
| 24 | =cut |
34 | =cut |
| 25 | |
35 | |
| 26 | ################################################## |
36 | ################################################## |
| … | |
… | |
| 36 | # |
46 | # |
| 37 | sub _check { |
47 | sub _check { |
| 38 | my $self = shift; |
48 | my $self = shift; |
| 39 | my $super = ref($self); $super =~ s/LimitedPolynomial/Parser/; |
49 | my $super = ref($self); $super =~ s/LimitedPolynomial/Parser/; |
| 40 | &{$super."::_check"}($self); |
50 | &{$super."::_check"}($self); |
| 41 | return if LimitedPolynomial::isConstant($self->{lop}) && |
51 | if (LimitedPolynomial::isConstant($self->{lop}) && |
| 42 | LimitedPolynomial::isConstant($self->{rop}); |
52 | LimitedPolynomial::isConstant($self->{rop})) { |
|
|
53 | $self->checkStrict if $self->context->flag("strictCoefficients"); |
|
|
54 | return; |
|
|
55 | } |
| 43 | return if $self->checkPolynomial; |
56 | return if $self->checkPolynomial; |
| 44 | $self->Error("Your answer doesn't look like a polynomial"); |
57 | $self->Error("Your answer doesn't look like a polynomial"); |
| 45 | } |
58 | } |
| 46 | |
59 | |
| 47 | # |
60 | # |
| … | |
… | |
| 93 | } |
106 | } |
| 94 | delete $r->{powers}; |
107 | delete $r->{powers}; |
| 95 | return 1; |
108 | return 1; |
| 96 | } |
109 | } |
| 97 | |
110 | |
|
|
111 | # |
|
|
112 | # Report an error when both operands are constants |
|
|
113 | # and strictCoefficients is in effect. |
|
|
114 | # |
|
|
115 | sub checkStrict { |
|
|
116 | my $self = shift; |
|
|
117 | $self->Error("Can't use '%s' between constants",$self->{bop}); |
|
|
118 | } |
|
|
119 | |
| 98 | ################################################## |
120 | ################################################## |
| 99 | |
121 | |
| 100 | package LimitedPolynomial; |
122 | package LimitedPolynomial; |
| 101 | |
123 | |
| 102 | # |
124 | # |
| … | |
… | |
| 155 | our @ISA = qw(LimitedPolynomial::BOP Parser::BOP::add); |
177 | our @ISA = qw(LimitedPolynomial::BOP Parser::BOP::add); |
| 156 | |
178 | |
| 157 | sub checkPolynomial { |
179 | sub checkPolynomial { |
| 158 | my $self = shift; |
180 | my $self = shift; |
| 159 | my ($l,$r) = ($self->{lop},$self->{rop}); |
181 | my ($l,$r) = ($self->{lop},$self->{rop}); |
| 160 | $self->Error("Addition is allowed only between monomials") |
182 | $self->Error("Addition is allowed only between monomials") if $r->{isPoly}; |
| 161 | if $r->{isPoly}; |
|
|
| 162 | $self->checkPowers; |
183 | $self->checkPowers; |
|
|
184 | } |
|
|
185 | |
|
|
186 | sub checkStrict { |
|
|
187 | my $self = shift; |
|
|
188 | $self->Error("You can only use addition for the terms of a polynomial",$self->{bop}); |
| 163 | } |
189 | } |
| 164 | |
190 | |
| 165 | ############################################## |
191 | ############################################## |
| 166 | |
192 | |
| 167 | package LimitedPolynomial::BOP::subtract; |
193 | package LimitedPolynomial::BOP::subtract; |
| 168 | our @ISA = qw(LimitedPolynomial::BOP Parser::BOP::subtract); |
194 | our @ISA = qw(LimitedPolynomial::BOP Parser::BOP::subtract); |
| 169 | |
195 | |
| 170 | sub checkPolynomial { |
196 | sub checkPolynomial { |
| 171 | my $self = shift; |
197 | my $self = shift; |
| 172 | my ($l,$r) = ($self->{lop},$self->{rop}); |
198 | my ($l,$r) = ($self->{lop},$self->{rop}); |
| 173 | $self->Error("Subtraction is only allowed between monomials") |
199 | $self->Error("Subtraction is allowed only between monomials") if $r->{isPoly}; |
| 174 | if $r->{isPoly}; |
|
|
| 175 | $self->checkPowers; |
200 | $self->checkPowers; |
|
|
201 | } |
|
|
202 | |
|
|
203 | sub checkStrict { |
|
|
204 | my $self = shift; |
|
|
205 | $self->Error("You can only use subtraction between the terms of a polynomial",$self->{bop}); |
| 176 | } |
206 | } |
| 177 | |
207 | |
| 178 | ############################################## |
208 | ############################################## |
| 179 | |
209 | |
| 180 | package LimitedPolynomial::BOP::multiply; |
210 | package LimitedPolynomial::BOP::multiply; |
| … | |
… | |
| 188 | my $rOK = ($r->{isPower} || $r->class eq 'Variable'); |
218 | my $rOK = ($r->{isPower} || $r->class eq 'Variable'); |
| 189 | return $self->checkExponents if $lOK and $rOK; |
219 | return $self->checkExponents if $lOK and $rOK; |
| 190 | $self->Error("Coefficients must come before variables in a polynomial") |
220 | $self->Error("Coefficients must come before variables in a polynomial") |
| 191 | if LimitedPolynomial::isConstant($r) && ($l->{isPower} || $l->class eq 'Variable'); |
221 | if LimitedPolynomial::isConstant($r) && ($l->{isPower} || $l->class eq 'Variable'); |
| 192 | $self->Error("Multiplication can only be used between coefficients and variables"); |
222 | $self->Error("Multiplication can only be used between coefficients and variables"); |
|
|
223 | } |
|
|
224 | |
|
|
225 | sub checkStrict { |
|
|
226 | my $self = shift; |
|
|
227 | $self->Error("You can only use '%s' between a coefficent and a variable in a polynomial",$self->{bop}); |
| 193 | } |
228 | } |
| 194 | |
229 | |
| 195 | ############################################## |
230 | ############################################## |
| 196 | |
231 | |
| 197 | package LimitedPolynomial::BOP::divide; |
232 | package LimitedPolynomial::BOP::divide; |
| … | |
… | |
| 206 | if $l->{isPoly} && $l->{isPoly} == 1; |
241 | if $l->{isPoly} && $l->{isPoly} == 1; |
| 207 | $self->{isPoly} = $l->{isPoly}; |
242 | $self->{isPoly} = $l->{isPoly}; |
| 208 | $self->{powers} = $l->{powers}; delete $l->{powers}; |
243 | $self->{powers} = $l->{powers}; delete $l->{powers}; |
| 209 | $self->{exponents} = $l->{exponents}; delete $l->{exponents}; |
244 | $self->{exponents} = $l->{exponents}; delete $l->{exponents}; |
| 210 | return 1; |
245 | return 1; |
|
|
246 | } |
|
|
247 | |
|
|
248 | sub checkStrict { |
|
|
249 | my $self = shift; |
|
|
250 | $self->Error("You can only use '%s' to form fractions",$self->{bop}) if $self->{lop}->class eq 'BOP'; |
| 211 | } |
251 | } |
| 212 | |
252 | |
| 213 | ############################################## |
253 | ############################################## |
| 214 | |
254 | |
| 215 | package LimitedPolynomial::BOP::power; |
255 | package LimitedPolynomial::BOP::power; |
| … | |
… | |
| 230 | LimitedPolynomial::markPowers($l); |
270 | LimitedPolynomial::markPowers($l); |
| 231 | $self->{exponents} = $l->{exponents}; delete $l->{exponents}; |
271 | $self->{exponents} = $l->{exponents}; delete $l->{exponents}; |
| 232 | foreach my $i (@{$self->{exponents}}) {$i = $n if $i} |
272 | foreach my $i (@{$self->{exponents}}) {$i = $n if $i} |
| 233 | $self->{isPower} = 1; |
273 | $self->{isPower} = 1; |
| 234 | return 1; |
274 | return 1; |
|
|
275 | } |
|
|
276 | |
|
|
277 | sub checkStrict { |
|
|
278 | my $self = shift; |
|
|
279 | $self->Error("You can only use powers of a variable in a polynomial"); |
| 235 | } |
280 | } |
| 236 | |
281 | |
| 237 | ############################################## |
282 | ############################################## |
| 238 | ############################################## |
283 | ############################################## |
| 239 | # |
284 | # |
| … | |
… | |
| 364 | # |
409 | # |
| 365 | # Don't convert -ax-b to -(ax+b), etc. |
410 | # Don't convert -ax-b to -(ax+b), etc. |
| 366 | # |
411 | # |
| 367 | $context->reduction->set("(-x)-y"=>0); |
412 | $context->reduction->set("(-x)-y"=>0); |
| 368 | |
413 | |
|
|
414 | # |
|
|
415 | # A context where coefficients can't include operations |
|
|
416 | # |
|
|
417 | $context = $main::context{"LimitedPolynomial-Strict"} = $context->copy; |
|
|
418 | $context->flags->set(strictCoefficients=>1, singelPowers=>1, reduceConstants=>0); |
|
|
419 | $context->functions->disable("All"); # can be re-enabled if needed |
|
|
420 | |
| 369 | main::Context("LimitedPolynomial"); ### FIXME: probably should require author to set this explicitly |
421 | main::Context("LimitedPolynomial"); ### FIXME: probably should require author to set this explicitly |
| 370 | } |
422 | } |
| 371 | |
423 | |
| 372 | 1; |
424 | 1; |