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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

1 : sh002i 5556 ################################################################################
2 :     # WeBWorK Online Homework Delivery System
3 :     # Copyright 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/
4 : gage 6058 # $CVSHeader$
5 : sh002i 5556 #
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 : sh002i 5551 =head1 NAME
18 : dpvc 2726
19 : sh002i 5551 contextLimitedComplex.pl - Allow complex numbers but not complex operations.
20 : dpvc 2726
21 : sh002i 5551 =head1 DESCRIPTION
22 : dpvc 2726
23 : sh002i 5551 Implements a context in which complex numbers can be entered,
24 :     but no complex operations are permitted. So students will
25 :     be able to perform operations within the real and imaginary
26 :     parts of the complex numbers, but not between complex numbers.
27 : gage 4808
28 : sh002i 5555 Context("LimitedComplex")
29 : sh002i 5551
30 :     Complex Numbers can still be entered in a+bi or a*e^(bt) form.
31 :     The e and i are allowed to be entered only once, so we have
32 :     to keep track of that, and allow SOME complex operations,
33 :     but only when one term is one of these constants (or an expression
34 :     involving it that we've already OKed).
35 :    
36 :     You control which format to use by setting the complex_format
37 :     context flag to 'cartesian', 'polar' or 'either'. E.g.,
38 :    
39 : sh002i 5555 Context()->flags->set(complex_format => 'polar');
40 : sh002i 5551
41 :     The default is 'either'. There are predefined contexts that
42 :     already have these values set:
43 :    
44 : sh002i 5555 Context("LimitedComplex-cartesian");
45 :     Context("LimitedComplex-polar");
46 : sh002i 5551
47 :     You can require that the a and b used in these forms be strictly
48 :     numbers (not expressions) by setting the strict_numeric flag and
49 :     disabling all the functions:
50 :    
51 : sh002i 5555 Context()->flags->set(strict_numeric=>1);
52 :     Context()->functions->disable('All');
53 : sh002i 5551
54 :     There are predefined contexts that already have these values
55 :     set:
56 :    
57 : sh002i 5555 Context("LimitedComplex-cartesian-strict");
58 :     Context("LimitedComplex-polar-strict");
59 :     Context("LimitedComplex-strict");
60 : sh002i 5551
61 : gage 4808 =cut
62 :    
63 : sh002i 5551 loadMacros("MathObjects.pl");
64 :    
65 :     sub _contextLimitedComplex_init {LimitedComplex::Init()}; # don't load it again
66 :    
67 : dpvc 5392 ##################################################
68 : dpvc 5048 #
69 :     # Handle common checking for BOPs
70 :     #
71 : dpvc 2726 package LimitedComplex::BOP;
72 :    
73 :     #
74 :     # Do original check and then if the operands are numbers, its OK.
75 :     # Otherwise, do an operator-specific check for if complex numbers are OK.
76 :     # Otherwise report an error.
77 :     #
78 :     sub _check {
79 :     my $self = shift;
80 :     my $super = ref($self); $super =~ s/LimitedComplex/Parser/;
81 :     &{$super."::_check"}($self);
82 : dpvc 3602 if ($self->{lop}->isRealNumber && $self->{rop}->isRealNumber) {
83 : dpvc 5048 return unless $self->context->{flags}{strict_numeric};
84 : dpvc 3602 } else {
85 :     Value::Error("The constant 'i' may appear only once in your formula")
86 :     if ($self->{lop}->isComplex and $self->{rop}->isComplex);
87 :     return if $self->checkComplex;
88 :     $self->Error("Exponential form is 'a*e^(bi)'")
89 :     if $self->{lop}{isPower} || $self->{rop}{isPower};
90 :     }
91 :     $self->Error("Your answer should be of the form %s",$self->theForm)
92 : dpvc 2726 }
93 :    
94 :     #
95 :     # filled in by subclasses
96 :     #
97 :     sub checkComplex {return 0}
98 :    
99 : dpvc 3602 #
100 :     # Get the form for use in error messages
101 :     #
102 :     sub theForm {
103 :     my $self = shift;
104 : dpvc 5048 my $format = $self->context->{flags}{complex_format};
105 : dpvc 3602 return 'a+bi' if $format eq 'cartesian';
106 :     return 'a*e^(bi)' if $format eq 'polar';
107 :     return 'a+bi or a*e^(bi)';
108 :     }
109 :    
110 : dpvc 2726 ##############################################
111 :     #
112 :     # Now we get the individual replacements for the operators
113 :     # that we don't want to allow. We inherit everything from
114 :     # the original Parser::BOP class, and just add the
115 :     # complex checks here. Note that checkComplex only
116 :     # gets called if exactly one of the terms is complex
117 :     # and the other is real.
118 :     #
119 :    
120 :     package LimitedComplex::BOP::add;
121 :     our @ISA = qw(LimitedComplex::BOP Parser::BOP::add);
122 :    
123 :     sub checkComplex {
124 :     my $self = shift;
125 : dpvc 5048 return 0 if $self->context->{flags}{complex_format} eq 'polar';
126 : dpvc 2726 my ($l,$r) = ($self->{lop},$self->{rop});
127 :     if ($l->isComplex) {my $tmp = $l; $l = $r; $r = $tmp};
128 :     return $r->class eq 'Constant' || $r->{isMult} ||
129 :     ($r->class eq 'Complex' && $r->{value}[0] == 0);
130 :     }
131 :    
132 :     ##############################################
133 :    
134 :     package LimitedComplex::BOP::subtract;
135 :     our @ISA = qw(LimitedComplex::BOP Parser::BOP::subtract);
136 :    
137 :     sub checkComplex {
138 :     my $self = shift;
139 : dpvc 5048 return 0 if $self->context->{flags}{complex_format} eq 'polar';
140 : dpvc 2726 my ($l,$r) = ($self->{lop},$self->{rop});
141 :     if ($l->isComplex) {my $tmp = $l; $l = $r; $r = $tmp};
142 :     return $r->class eq 'Constant' || $r->{isMult} ||
143 :     ($r->class eq 'Complex' && $r->{value}[0] == 0);
144 :     }
145 :    
146 :     ##############################################
147 :    
148 :     package LimitedComplex::BOP::multiply;
149 :     our @ISA = qw(LimitedComplex::BOP Parser::BOP::multiply);
150 :    
151 :     sub checkComplex {
152 :     my $self = shift;
153 :     my ($l,$r) = ($self->{lop},$self->{rop});
154 :     $self->{isMult} = !$r->{isPower};
155 :     return (($l->class eq 'Constant' || $l->isRealNumber) &&
156 :     ($r->class eq 'Constant' || $r->isRealNumber || $r->{isPower}));
157 :     }
158 :    
159 :     ##############################################
160 :    
161 :     package LimitedComplex::BOP::divide;
162 :     our @ISA = qw(LimitedComplex::BOP Parser::BOP::divide);
163 :    
164 :     ##############################################
165 :    
166 :     package LimitedComplex::BOP::power;
167 :     our @ISA = qw(LimitedComplex::BOP Parser::BOP::power);
168 :    
169 :     #
170 :     # Base must be 'e' (then we know the other is the complex
171 :     # since we only get here if exactly one term is complex)
172 :     #
173 :     sub checkComplex {
174 :     my $self = shift;
175 : dpvc 5048 return 0 if $self->context->{flags}{complex_format} eq 'cartesian';
176 : dpvc 2726 my ($l,$r) = ($self->{lop},$self->{rop});
177 :     $self->{isPower} = 1;
178 :     return 1 if ($l->class eq 'Constant' && $l->{name} eq 'e' &&
179 : dpvc 3602 ($r->class eq 'Constant' || $r->{isMult} || $r->{isOp} ||
180 : dpvc 2726 $r->class eq 'Complex' && $r->{value}[0] == 0));
181 :     $self->Error("Exponentials can only be of the form 'e^(ai)' in this context");
182 :     }
183 :    
184 :     ##############################################
185 :     ##############################################
186 :     #
187 :     # Now we do the same for the unary operators
188 :     #
189 :    
190 :     package LimitedComplex::UOP;
191 :    
192 :     sub _check {
193 :     my $self = shift;
194 :     my $super = ref($self); $super =~ s/LimitedComplex/Parser/;
195 :     &{$super."::_check"}($self);
196 : dpvc 3602 my $op = $self->{op}; $self->{isOp} = 1;
197 :     if ($op->isRealNumber) {
198 : dpvc 5048 return unless $self->context->{flags}{strict_numeric};
199 : dpvc 3602 return if $op->class eq 'Number';
200 :     } else {
201 :     return if $self->{op}{isMult} || $self->{op}{isPower};
202 :     return if $op->class eq 'Constant' && $op->{name} eq 'i';
203 :     }
204 :     $self->Error("Your answer should be of the form %s",$self->theForm)
205 : dpvc 2726 }
206 :    
207 :     sub checkComplex {return 0}
208 :    
209 : dpvc 3602 sub theForm {LimitedComplex::BOP::theForm(@_)}
210 :    
211 : dpvc 2726 ##############################################
212 :    
213 :     package LimitedComplex::UOP::plus;
214 :     our @ISA = qw(LimitedComplex::UOP Parser::UOP::plus);
215 :    
216 :     ##############################################
217 :    
218 :     package LimitedComplex::UOP::minus;
219 :     our @ISA = qw(LimitedComplex::UOP Parser::UOP::minus);
220 :    
221 :     ##############################################
222 :     ##############################################
223 :     #
224 :     # Absolute value does complex norm, so we
225 :     # trap that as well.
226 :     #
227 :    
228 :     package LimitedComplex::List::AbsoluteValue;
229 :     our @ISA = qw(Parser::List::AbsoluteValue);
230 :    
231 :     sub _check {
232 :     my $self = shift;
233 :     $self->SUPER::_check;
234 :     return if $self->{coords}[0]->isRealNumber;
235 :     $self->Error("Can't take absolute value of Complex Numbers in this context");
236 :     }
237 :    
238 :     ##############################################
239 :     ##############################################
240 :    
241 : dpvc 5392 package LimitedComplex;
242 : dpvc 2726
243 : dpvc 5392 sub Init {
244 : dpvc 2726
245 : dpvc 5392 #
246 :     # Build the new context that calls the
247 :     # above classes rather than the usual ones
248 :     #
249 : dpvc 2726
250 : dpvc 5392 my $context = $main::context{LimitedComplex} = Parser::Context->getCopy("Complex");
251 : dpvc 5441 $context->{name} = "LimitedComplex";
252 : dpvc 5392 $context->operators->set(
253 :     '+' => {class => 'LimitedComplex::BOP::add'},
254 :     '-' => {class => 'LimitedComplex::BOP::subtract'},
255 :     '*' => {class => 'LimitedComplex::BOP::multiply'},
256 :     '* ' => {class => 'LimitedComplex::BOP::multiply'},
257 :     ' *' => {class => 'LimitedComplex::BOP::multiply'},
258 :     ' ' => {class => 'LimitedComplex::BOP::multiply'},
259 :     '/' => {class => 'LimitedComplex::BOP::divide'},
260 :     ' /' => {class => 'LimitedComplex::BOP::divide'},
261 :     '/ ' => {class => 'LimitedComplex::BOP::divide'},
262 :     '^' => {class => 'LimitedComplex::BOP::power'},
263 :     '**' => {class => 'LimitedComplex::BOP::power'},
264 :     'u+' => {class => 'LimitedComplex::UOP::plus'},
265 :     'u-' => {class => 'LimitedComplex::UOP::minus'},
266 :     );
267 :     #
268 :     # Remove these operators and functions
269 :     #
270 :     $context->lists->set(
271 :     AbsoluteValue => {class => 'LimitedComplex::List::AbsoluteValue'},
272 :     );
273 :     $context->operators->undefine('_','U');
274 :     $context->functions->disable('Complex');
275 :     foreach my $fn ($context->functions->names) {$context->{functions}{$fn}{nocomplex} = 1}
276 :     #
277 :     # Format can be 'cartesian', 'polar', or 'either'
278 :     #
279 :     $context->flags->set(complex_format => 'either');
280 : dpvc 2726
281 : dpvc 5392 #########################
282 : dpvc 2726
283 : dpvc 5392 $context = $main::context{'LimitedComplex-cartesian'} = $main::context{LimitedComplex}->copy;
284 :     $context->flags->set(complex_format => 'cartesian');
285 : dpvc 3602
286 : dpvc 5392 #########################
287 : dpvc 3602
288 : dpvc 5392 $context = $main::context{'LimitedComplex-polar'} = $main::context{LimitedComplex}->copy;
289 :     $context->flags->set(complex_format => 'polar');
290 : dpvc 3602
291 : dpvc 5392 #########################
292 : dpvc 5373
293 : dpvc 5392 $context = $main::context{'LimitedComplex-cartesian-strict'} = $main::context{'LimitedComplex-cartesian'}->copy;
294 :     $context->flags->set(strict_numeric => 1);
295 :     $context->functions->disable('All');
296 :    
297 :     #########################
298 :    
299 :     $context = $main::context{'LimitedComplex-polar-strict'} = $main::context{'LimitedComplex-polar'}->copy;
300 :     $context->flags->set(strict_numeric => 1);
301 :     $context->functions->disable('All');
302 :    
303 :     #########################
304 :    
305 :     $context = $main::context{'LimitedComplex-strict'} = $main::context{'LimitedComplex'}->copy;
306 :     $context->flags->set(strict_numeric => 1);
307 :     $context->functions->disable('All');
308 :    
309 :     #########################
310 :    
311 :     main::Context("LimitedComplex"); ### FIXME: probably should require the author to set this explicitly
312 :     }
313 :    
314 : dpvc 5373 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9