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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9