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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3602 - (download) (as text) (annotate)
Wed Sep 7 01:06:13 2005 UTC (14 years, 4 months ago) by dpvc
File size: 9207 byte(s)
Added "strict" versions of the limited complex contexts that don't
allow operations within the real and imaginary parts.  Also cleaned up
some of the code and fixed a few situations that had been missed
before.

Note that in strict mode, e^(pi i) is accepted, but e^(pi/2 i) is
not.  Should that be changed?  It would be possible to make a context
in which a*e^(b*pi/c i) is accepted.

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9