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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

1 : dpvc 6216 ################################################################################
2 :     # WeBWorK Online Homework Delivery System
3 :     # Copyright 2000-2010 The WeBWorK Project, http://openwebwork.sf.net/
4 :     # $CVSHeader: pg/macros/contextLimitedPolynomial.pl,v 1.23 2009/06/25 23:28:44 gage 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 :     contextPolynomialFactors.pl - Allow only entry of polynomials, and
20 :     their products and powers
21 :    
22 :     =head1 DESCRIPTION
23 :    
24 :     Implements a context in which students can only enter products and
25 :     powers of polynomials
26 :    
27 :     Select the context using:
28 :    
29 :     Context("PolynomialFactors");
30 :    
31 :     If you set the "singlePowers" flag, then only one monomial of each
32 :     degree can be included in each factor polynomial:
33 :    
34 :     Context("PolynomialFactors")->flags->set(singlePowers=>1);
35 :    
36 :     If you set the "singleFactors" flag, then factors can not be repeated.
37 :     For example,
38 :    
39 :     Context("PolynomialFactors")->flags->set(singleFactors=>1);
40 :     Formula("(x+1)^2*(x+1)");
41 :    
42 :     will generate an error indicating that factors can appear only once.
43 :     Note, however, that this only catches factors that appear exactly the
44 :     same in both cases, so (x+1)*(1+x) would be allowed, as would
45 :     -(x-1)*(1-x). Note also that there is no check for whether the
46 :     factors are irreducible, so (x^2-1)*(x+1)*(x-1) would be allowed.
47 :     Also, there is no check for whether constants have been factored out,
48 :     so 3*(x+1)*(3x+3) would be allowed. This still needs more work to
49 :     make it very useful.
50 :    
51 :     There are two additional flags that control whether division by a
52 :     constant or raising to a power are allowed to be performed on a
53 :     product or factors or only on a single factor at at time. These are
54 :     strictDivision and strictPowers. By default, strictDivisions is 0, so
55 :     (x*(x+1))/3 is allowed, while strictPowers is 1, so (x*(x+1))^3 is not
56 :     (it must be written x^3*(x+1)^3).
57 :    
58 :     Finally, there is also a strict limited context that does not allow
59 :     operations even within the coefficients. Select it using:
60 :    
61 :     Context("PolynomialFactors-Strict");
62 :    
63 :     In addition to disallowing operations within the coefficients, this
64 :     context does not reduce constant operations (since they are not
65 :     allowed), and sets the singlePowers, singleFactors, strictDivision,
66 :     and stricPowers flags automatically. In addition, it disables all the
67 :     functions, though they can be re-enabled, if needed.
68 :    
69 :     =cut
70 :    
71 :     ##################################################
72 :    
73 :     loadMacros(
74 :     "MathObjects.pl",
75 :     "contextLimitedPolynomial.pl"
76 :     );
77 :    
78 :     sub _contextPolynomialFactors_init {PolynomialFactors::Init()}
79 :    
80 :     ##############################################
81 :    
82 :     package PolynomialFactors::BOP::add;
83 :     our @ISA = qw(LimitedPolynomial::BOP::add);
84 :    
85 :     sub checkPolynomial {
86 :     my $self = shift;
87 :     my ($l,$r) = ($self->{lop},$self->{rop});
88 :     $self->Error("Addition is allowed only between monomials")
89 :     if $r->{isPoly} || ($l->{isPoly} && $l->{isPoly} > 2);
90 :     $self->checkPowers;
91 :     }
92 :    
93 :     ##############################################
94 :    
95 :     package PolynomialFactors::BOP::subtract;
96 :     our @ISA = qw(LimitedPolynomial::BOP::subtract);
97 :    
98 :     sub checkPolynomial {
99 :     my $self = shift;
100 :     my ($l,$r) = ($self->{lop},$self->{rop});
101 :     $self->Error("Subtraction is allowed only between monomials")
102 :     if $r->{isPoly} || ($l->{isPoly} && $l->{isPoly} > 2);
103 :     $self->checkPowers;
104 :     }
105 :    
106 :     ##############################################
107 :    
108 :     package PolynomialFactors::BOP::multiply;
109 :     our @ISA = qw(PolynomialFactors::BOP LimitedPolynomial::BOP::multiply);
110 :    
111 :     sub checkPolynomial {
112 :     my $self = shift;
113 :     my ($l,$r) = ($self->{lop},$self->{rop});
114 :     my $lOK = (LimitedPolynomial::isConstant($l) || $l->{isPower} ||
115 :     $l->class eq 'Variable' || ($l->{isPoly} && $l->{isPoly} == 2));
116 :     my $rOK = ($r->{isPower} || $r->class eq 'Variable');
117 :     return $self->checkExponents if $lOK and $rOK;
118 :     $self->Error("Coefficients must come before variables or factors")
119 :     if LimitedPolynomial::isConstant($r) && ($l->{isPower} || $l->class eq 'Variable');
120 :     if ($l->{isPoly} || $r->{isPoly}) {
121 :     PolynomialFactors::markFactor($l);
122 :     PolynomialFactors::markFactor($r);
123 :     return $self->checkFactors($l,$r);
124 :     }
125 :     return 1;
126 :     }
127 :    
128 :     sub checkFactors {
129 :     my $self = shift; my ($l,$r) = @_;
130 :     my $single = $self->context->flag("singleFactors");
131 :     $self->{factors} = $l->{factors}; delete $l->{factors};
132 :     foreach my $factor (keys %{$r->{factors}}) {
133 :     if ($single && $self->{factors}{$factor}) {
134 :     $self->Error("Each factor can appear only once (combine like factors)") unless $factor eq "0";
135 :     $self->Error("Only one constant coefficient or negation is allowed (combine them)");
136 :     }
137 :     $self->{factors}{$factor} = 1;
138 :     }
139 :     $self->{isPoly} = 4; # product of factors
140 :     return 1;
141 :     }
142 :    
143 :     sub checkStrict {
144 :     my $self = shift;
145 :     $self->Error("You can only use '%s' between coefficents and variables or between factors",$self->{bop});
146 :     }
147 :    
148 :     ##############################################
149 :    
150 :     package PolynomialFactors::BOP::divide;
151 :     our @ISA = qw(LimitedPolynomial::BOP::divide);
152 :    
153 :     sub checkPolynomial {
154 :     my $self = shift;
155 :     my ($l,$r) = ($self->{lop},$self->{rop});
156 :     $self->Error("In a polynomial, you can only divide by numbers")
157 :     unless LimitedPolynomial::isConstant($r);
158 :     if ($l->{isPoly} && $l->{isPoly} != 2) {
159 :     $self->Error("You can only divide a single term or factor by a number")
160 :     if $l->{isPoly} == 3 || ($self->context->flag("strictDivision") && $self->{isPoly} != 1);
161 :     PolynomialFactors::markFactor($l);
162 :     $self->Error("Only one constant multiple or fraction is allowed (combine them)")
163 :     if $l->{factors}{0} && $self->context->flag("singleFactors");
164 :     $self->{factors} = $l->{factors}; delete $l->{factors};
165 :     $self->{factors}{0} = 1; # mark as constant multiple;
166 :     $self->{isPoly} = 3; # factor over a number
167 :     } else {
168 :     $self->{isPoly} = $l->{isPoly};
169 :     $self->{powers} = $l->{powers}; delete $l->{powers};
170 :     $self->{exponents} = $l->{exponents}; delete $l->{exponents};
171 :     }
172 :     return 1;
173 :     }
174 :    
175 :     ##############################################
176 :    
177 :     package PolynomialFactors::BOP::power;
178 :     our @ISA = qw(LimitedPolynomial::BOP::power);
179 :    
180 :     sub checkPolynomial {
181 :     my $self = shift;
182 :     my ($l,$r) = ($self->{lop},$self->{rop});
183 :     $self->Error("Exponents must be constant in a polynomial")
184 :     unless LimitedPolynomial::isConstant($r);
185 :     my $n = Parser::Evaluate($r);
186 :     $r->Error($$Value::context->{error}{message}) if $$Value::context->{error}{flag};
187 :     $n = $n->value;
188 :     $self->Error("Exponents must be positive integers in a polynomial")
189 :     unless $n > 0 && $n == int($n);
190 :     if ($l->{isPoly}) {
191 :     $self->Error("You can only raise a single term or factor to a power")
192 :     if $l->{isPoly} > 2 && $self->context->flag("strictPowers");
193 :     PolynomialFactors::markFactor($l);
194 :     $self->{factors} = $l->{factors}; delete $l->{factors};
195 :     $self->{isPoly} = 5; # factor to a power
196 :     } else {
197 :     LimitedPolynomial::markPowers($l);
198 :     $self->{exponents} = $l->{exponents}; delete $l->{exponents};
199 :     foreach my $i (@{$self->{exponents}}) {$i = $n if $i}
200 :     $self->{isPower} = 1;
201 :     }
202 :     return 1;
203 :     }
204 :    
205 :     sub checkStrict {
206 :     my $self = shift;
207 :     $self->Error("You can only use powers of a variable or factor");
208 :     }
209 :    
210 :     ##############################################
211 :    
212 :     package PolynomialFactors::UOP::minus;
213 :     our @ISA = qw(LimitedPolynomial::UOP::minus);
214 :    
215 :     sub checkPolynomial {
216 :     my $self = shift; my $op = $self->{op};
217 :     if ($op->{isPoly} && $self->context->flag("singleFactors")) {
218 :     $self->Error("Double negatives are not allowed") if $op->{isPoly} == 2;
219 :     $self->Error("Only one factor or constant can be negated")
220 :     if $op->{isPoly} != 1 && $op->{isPoly} != 5;
221 :     }
222 :     PolynomialFactors::markFactor($op);
223 :     $self->{factors} = $op->{factors}; delete $op->{factors};
224 :     $self->{factors}{0} = 1; # mark as constant multiple
225 :     return 1;
226 :     }
227 :    
228 :     ##############################################
229 :    
230 :     package PolynomialFactors;
231 :     our @ISA = ('LimitedPolynomal');
232 :    
233 :     sub markFactor {
234 :     my $self = shift;
235 :     return if $self->{factors};
236 :     $self->{factors} = {};
237 :     if ($self->class eq 'Variable') {
238 :     $self->{factors}{$self->{name}} = 1;
239 :     } elsif ($self->class eq 'Number') {
240 :     $self->{factors}{0} = 1;
241 :     } elsif ($self->{isPoly} && $self->{isPoly} == 1) {
242 :     $self->{factors}{$self->string} = 1;
243 :     } elsif ($self->{isPower}) {
244 :     $self->{factors}{$self->{lop}->string} = 1;
245 :     }
246 :     }
247 :    
248 :     sub Init {
249 :     #
250 :     # Build the new context that calls the
251 :     # above classes rather than the usual ones
252 :     #
253 :    
254 :     my $context = $main::context{PolynomialFactors} = Parser::Context->getCopy("LimitedPolynomial");
255 :     $context->{name} = "PolynomialFactors";
256 :     $context->operators->set(
257 :     '+' => {class => 'PolynomialFactors::BOP::add'},
258 :     '-' => {class => 'PolynomialFactors::BOP::subtract'},
259 :     '*' => {class => 'PolynomialFactors::BOP::multiply'},
260 :     '* ' => {class => 'PolynomialFactors::BOP::multiply'},
261 :     ' *' => {class => 'PolynomialFactors::BOP::multiply'},
262 :     ' ' => {class => 'PolynomialFactors::BOP::multiply'},
263 :     '/' => {class => 'PolynomialFactors::BOP::divide'},
264 :     ' /' => {class => 'PolynomialFactors::BOP::divide'},
265 :     '/ ' => {class => 'PolynomialFactors::BOP::divide'},
266 :     '^' => {class => 'PolynomialFactors::BOP::power'},
267 :     '**' => {class => 'PolynomialFactors::BOP::power'},
268 :     'u-' => {class => 'PolynomialFactors::UOP::minus'},
269 :     );
270 :     $context->flags->set(strictPowers => 1);
271 :    
272 :     #
273 :     # A context where coefficients can't include operations
274 :     #
275 :     $context = $main::context{"PolynomialFactors-Strict"} = $context->copy;
276 :     $context->flags->set(
277 :     strictCoefficients => 1, strictDivision => 1,
278 :     singlePowers => 1, singleFactors => 1,
279 :     reduceConstants => 0,
280 :     );
281 :     $context->functions->disable("All"); # can be re-enabled if needed
282 :    
283 :     main::Context("PolynomialFactors"); ### FIXME: probably should require author to set this explicitly
284 :     }
285 :    
286 :     1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9