[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 5370 - (view) (download) (as text)

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9