Parent Directory
|
Revision Log
Updated to use new context methods.
1 loadMacros("Parser.pl"); 2 3 sub _contextLimitedComplex_init {}; # don't load it again 4 5 =head3 Context("LimitedComplex") 6 7 ########################################################## 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 # 26 # 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 # 50 # Handle common checking for BOPs 51 # 52 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 if ($self->{lop}->isRealNumber && $self->{rop}->isRealNumber) { 64 return unless $self->context->{flags}{strict_numeric}; 65 } 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 } 74 75 # 76 # filled in by subclasses 77 # 78 sub checkComplex {return 0} 79 80 # 81 # Get the form for use in error messages 82 # 83 sub theForm { 84 my $self = shift; 85 my $format = $self->context->{flags}{complex_format}; 86 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 ############################################## 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 return 0 if $self->context->{flags}{complex_format} eq 'polar'; 107 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 return 0 if $self->context->{flags}{complex_format} eq 'polar'; 121 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 return 0 if $self->context->{flags}{complex_format} eq 'cartesian'; 157 my ($l,$r) = ($self->{lop},$self->{rop}); 158 $self->{isPower} = 1; 159 return 1 if ($l->class eq 'Constant' && $l->{name} eq 'e' && 160 ($r->class eq 'Constant' || $r->{isMult} || $r->{isOp} || 161 $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 my $op = $self->{op}; $self->{isOp} = 1; 178 if ($op->isRealNumber) { 179 return unless $self->context->{flags}{strict_numeric}; 180 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 } 187 188 sub checkComplex {return 0} 189 190 sub theForm {LimitedComplex::BOP::theForm(@_)} 191 192 ############################################## 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 $context{LimitedComplex} = Parser::Context->getCopy(undef,"Complex"); 230 $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 $context{LimitedComplex}->functions->disable('Complex'); 253 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 $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 Context("LimitedComplex");
| aubreyja at gmail dot com | ViewVC Help |
| Powered by ViewVC 1.0.9 |