[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 5441 - (download) (as text) (annotate)
Tue Aug 28 22:40:15 2007 UTC (12 years, 4 months ago) by dpvc
File size: 9513 byte(s)
Add context names for the context(s) created here.

    1 loadMacros("MathObjects.pl");
    2 
    3 sub _contextLimitedComplex_init {LimitedComplex::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 #
   51 #  Handle common checking for BOPs
   52 #
   53 package LimitedComplex::BOP;
   54 
   55 #
   56 #  Do original check and then if the operands are numbers, its OK.
   57 #  Otherwise, do an operator-specific check for if complex numbers are OK.
   58 #  Otherwise report an error.
   59 #
   60 sub _check {
   61   my $self = shift;
   62   my $super = ref($self); $super =~ s/LimitedComplex/Parser/;
   63   &{$super."::_check"}($self);
   64   if ($self->{lop}->isRealNumber && $self->{rop}->isRealNumber) {
   65     return unless $self->context->{flags}{strict_numeric};
   66   } else {
   67     Value::Error("The constant 'i' may appear only once in your formula")
   68       if ($self->{lop}->isComplex and $self->{rop}->isComplex);
   69     return if $self->checkComplex;
   70     $self->Error("Exponential form is 'a*e^(bi)'")
   71       if $self->{lop}{isPower} || $self->{rop}{isPower};
   72   }
   73   $self->Error("Your answer should be of the form %s",$self->theForm)
   74 }
   75 
   76 #
   77 #  filled in by subclasses
   78 #
   79 sub checkComplex {return 0}
   80 
   81 #
   82 #  Get the form for use in error messages
   83 #
   84 sub theForm {
   85   my $self = shift;
   86   my $format = $self->context->{flags}{complex_format};
   87   return 'a+bi' if $format eq 'cartesian';
   88   return 'a*e^(bi)' if $format eq 'polar';
   89   return 'a+bi or a*e^(bi)';
   90 }
   91 
   92 ##############################################
   93 #
   94 #  Now we get the individual replacements for the operators
   95 #  that we don't want to allow.  We inherit everything from
   96 #  the original Parser::BOP class, and just add the
   97 #  complex checks here.  Note that checkComplex only
   98 #  gets called if exactly one of the terms is complex
   99 #  and the other is real.
  100 #
  101 
  102 package LimitedComplex::BOP::add;
  103 our @ISA = qw(LimitedComplex::BOP Parser::BOP::add);
  104 
  105 sub checkComplex {
  106   my $self = shift;
  107   return 0 if $self->context->{flags}{complex_format} eq 'polar';
  108   my ($l,$r) = ($self->{lop},$self->{rop});
  109   if ($l->isComplex) {my $tmp = $l; $l = $r; $r = $tmp};
  110   return $r->class eq 'Constant' || $r->{isMult} ||
  111     ($r->class eq 'Complex' && $r->{value}[0] == 0);
  112 }
  113 
  114 ##############################################
  115 
  116 package LimitedComplex::BOP::subtract;
  117 our @ISA = qw(LimitedComplex::BOP Parser::BOP::subtract);
  118 
  119 sub checkComplex {
  120   my $self = shift;
  121   return 0 if $self->context->{flags}{complex_format} eq 'polar';
  122   my ($l,$r) = ($self->{lop},$self->{rop});
  123   if ($l->isComplex) {my $tmp = $l; $l = $r; $r = $tmp};
  124   return $r->class eq 'Constant' || $r->{isMult} ||
  125     ($r->class eq 'Complex' && $r->{value}[0] == 0);
  126 }
  127 
  128 ##############################################
  129 
  130 package LimitedComplex::BOP::multiply;
  131 our @ISA = qw(LimitedComplex::BOP Parser::BOP::multiply);
  132 
  133 sub checkComplex {
  134   my $self = shift;
  135   my ($l,$r) = ($self->{lop},$self->{rop});
  136   $self->{isMult} = !$r->{isPower};
  137   return (($l->class eq 'Constant' || $l->isRealNumber) &&
  138     ($r->class eq 'Constant' || $r->isRealNumber || $r->{isPower}));
  139 }
  140 
  141 ##############################################
  142 
  143 package LimitedComplex::BOP::divide;
  144 our @ISA = qw(LimitedComplex::BOP Parser::BOP::divide);
  145 
  146 ##############################################
  147 
  148 package LimitedComplex::BOP::power;
  149 our @ISA = qw(LimitedComplex::BOP Parser::BOP::power);
  150 
  151 #
  152 #  Base must be 'e' (then we know the other is the complex
  153 #  since we only get here if exactly one term is complex)
  154 #
  155 sub checkComplex {
  156   my $self = shift;
  157   return 0 if $self->context->{flags}{complex_format} eq 'cartesian';
  158   my ($l,$r) = ($self->{lop},$self->{rop});
  159   $self->{isPower} = 1;
  160   return 1 if ($l->class eq 'Constant' && $l->{name} eq 'e' &&
  161          ($r->class eq 'Constant' || $r->{isMult} || $r->{isOp} ||
  162     $r->class eq 'Complex' && $r->{value}[0] == 0));
  163   $self->Error("Exponentials can only be of the form 'e^(ai)' in this context");
  164 }
  165 
  166 ##############################################
  167 ##############################################
  168 #
  169 #  Now we do the same for the unary operators
  170 #
  171 
  172 package LimitedComplex::UOP;
  173 
  174 sub _check {
  175   my $self = shift;
  176   my $super = ref($self); $super =~ s/LimitedComplex/Parser/;
  177   &{$super."::_check"}($self);
  178   my $op = $self->{op}; $self->{isOp} = 1;
  179   if ($op->isRealNumber) {
  180     return unless $self->context->{flags}{strict_numeric};
  181     return if $op->class eq 'Number';
  182   } else {
  183     return if $self->{op}{isMult} || $self->{op}{isPower};
  184     return if $op->class eq 'Constant' && $op->{name} eq 'i';
  185   }
  186   $self->Error("Your answer should be of the form %s",$self->theForm)
  187 }
  188 
  189 sub checkComplex {return 0}
  190 
  191 sub theForm {LimitedComplex::BOP::theForm(@_)}
  192 
  193 ##############################################
  194 
  195 package LimitedComplex::UOP::plus;
  196 our @ISA = qw(LimitedComplex::UOP Parser::UOP::plus);
  197 
  198 ##############################################
  199 
  200 package LimitedComplex::UOP::minus;
  201 our @ISA = qw(LimitedComplex::UOP Parser::UOP::minus);
  202 
  203 ##############################################
  204 ##############################################
  205 #
  206 #  Absolute value does complex norm, so we
  207 #  trap that as well.
  208 #
  209 
  210 package LimitedComplex::List::AbsoluteValue;
  211 our @ISA = qw(Parser::List::AbsoluteValue);
  212 
  213 sub _check {
  214   my $self = shift;
  215   $self->SUPER::_check;
  216   return if $self->{coords}[0]->isRealNumber;
  217   $self->Error("Can't take absolute value of Complex Numbers in this context");
  218 }
  219 
  220 ##############################################
  221 ##############################################
  222 
  223 package LimitedComplex;
  224 
  225 sub Init {
  226 
  227   #
  228   #  Build the new context that calls the
  229   #  above classes rather than the usual ones
  230   #
  231 
  232   my $context = $main::context{LimitedComplex} = Parser::Context->getCopy("Complex");
  233   $context->{name} = "LimitedComplex";
  234   $context->operators->set(
  235      '+' => {class => 'LimitedComplex::BOP::add'},
  236      '-' => {class => 'LimitedComplex::BOP::subtract'},
  237      '*' => {class => 'LimitedComplex::BOP::multiply'},
  238     '* ' => {class => 'LimitedComplex::BOP::multiply'},
  239     ' *' => {class => 'LimitedComplex::BOP::multiply'},
  240      ' ' => {class => 'LimitedComplex::BOP::multiply'},
  241      '/' => {class => 'LimitedComplex::BOP::divide'},
  242     ' /' => {class => 'LimitedComplex::BOP::divide'},
  243     '/ ' => {class => 'LimitedComplex::BOP::divide'},
  244      '^' => {class => 'LimitedComplex::BOP::power'},
  245     '**' => {class => 'LimitedComplex::BOP::power'},
  246     'u+' => {class => 'LimitedComplex::UOP::plus'},
  247     'u-' => {class => 'LimitedComplex::UOP::minus'},
  248   );
  249   #
  250   #  Remove these operators and functions
  251   #
  252   $context->lists->set(
  253     AbsoluteValue => {class => 'LimitedComplex::List::AbsoluteValue'},
  254   );
  255   $context->operators->undefine('_','U');
  256   $context->functions->disable('Complex');
  257   foreach my $fn ($context->functions->names) {$context->{functions}{$fn}{nocomplex} = 1}
  258   #
  259   #  Format can be 'cartesian', 'polar', or 'either'
  260   #
  261   $context->flags->set(complex_format => 'either');
  262 
  263   #########################
  264 
  265   $context = $main::context{'LimitedComplex-cartesian'} = $main::context{LimitedComplex}->copy;
  266   $context->flags->set(complex_format => 'cartesian');
  267 
  268   #########################
  269 
  270   $context = $main::context{'LimitedComplex-polar'} = $main::context{LimitedComplex}->copy;
  271   $context->flags->set(complex_format => 'polar');
  272 
  273   #########################
  274 
  275   $context = $main::context{'LimitedComplex-cartesian-strict'} = $main::context{'LimitedComplex-cartesian'}->copy;
  276   $context->flags->set(strict_numeric => 1);
  277   $context->functions->disable('All');
  278 
  279   #########################
  280 
  281   $context = $main::context{'LimitedComplex-polar-strict'} = $main::context{'LimitedComplex-polar'}->copy;
  282   $context->flags->set(strict_numeric => 1);
  283   $context->functions->disable('All');
  284 
  285   #########################
  286 
  287   $context = $main::context{'LimitedComplex-strict'} = $main::context{'LimitedComplex'}->copy;
  288   $context->flags->set(strict_numeric => 1);
  289   $context->functions->disable('All');
  290 
  291   #########################
  292 
  293   main::Context("LimitedComplex");  ### FIXME:  probably should require the author to set this explicitly
  294 }
  295 
  296 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9