[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 6058 - (download) (as text) (annotate)
Thu Jun 25 23:28:44 2009 UTC (10 years, 6 months ago) by gage
File size: 10279 byte(s)
syncing pg HEAD with pg2.4.7 on 6/25/2009

    1 ################################################################################
    2 # WeBWorK Online Homework Delivery System
    3 # Copyright  2000-2007 The WeBWorK Project, http://openwebwork.sf.net/
    4 # $CVSHeader$
    5 #
    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 =head1 NAME
   18 
   19 contextLimitedComplex.pl - Allow complex numbers but not complex operations.
   20 
   21 =head1 DESCRIPTION
   22 
   23 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 
   28   Context("LimitedComplex")
   29 
   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   Context()->flags->set(complex_format => 'polar');
   40 
   41 The default is 'either'.  There are predefined contexts that
   42 already have these values set:
   43 
   44   Context("LimitedComplex-cartesian");
   45   Context("LimitedComplex-polar");
   46 
   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   Context()->flags->set(strict_numeric=>1);
   52   Context()->functions->disable('All');
   53 
   54 There are predefined contexts that already have these values
   55 set:
   56 
   57   Context("LimitedComplex-cartesian-strict");
   58   Context("LimitedComplex-polar-strict");
   59   Context("LimitedComplex-strict");
   60 
   61 =cut
   62 
   63 loadMacros("MathObjects.pl");
   64 
   65 sub _contextLimitedComplex_init {LimitedComplex::Init()}; # don't load it again
   66 
   67 ##################################################
   68 #
   69 #  Handle common checking for BOPs
   70 #
   71 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   if ($self->{lop}->isRealNumber && $self->{rop}->isRealNumber) {
   83     return unless $self->context->{flags}{strict_numeric};
   84   } 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 }
   93 
   94 #
   95 #  filled in by subclasses
   96 #
   97 sub checkComplex {return 0}
   98 
   99 #
  100 #  Get the form for use in error messages
  101 #
  102 sub theForm {
  103   my $self = shift;
  104   my $format = $self->context->{flags}{complex_format};
  105   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 ##############################################
  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   return 0 if $self->context->{flags}{complex_format} eq 'polar';
  126   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   return 0 if $self->context->{flags}{complex_format} eq 'polar';
  140   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   return 0 if $self->context->{flags}{complex_format} eq 'cartesian';
  176   my ($l,$r) = ($self->{lop},$self->{rop});
  177   $self->{isPower} = 1;
  178   return 1 if ($l->class eq 'Constant' && $l->{name} eq 'e' &&
  179          ($r->class eq 'Constant' || $r->{isMult} || $r->{isOp} ||
  180     $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   my $op = $self->{op}; $self->{isOp} = 1;
  197   if ($op->isRealNumber) {
  198     return unless $self->context->{flags}{strict_numeric};
  199     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 }
  206 
  207 sub checkComplex {return 0}
  208 
  209 sub theForm {LimitedComplex::BOP::theForm(@_)}
  210 
  211 ##############################################
  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 package LimitedComplex;
  242 
  243 sub Init {
  244 
  245   #
  246   #  Build the new context that calls the
  247   #  above classes rather than the usual ones
  248   #
  249 
  250   my $context = $main::context{LimitedComplex} = Parser::Context->getCopy("Complex");
  251   $context->{name} = "LimitedComplex";
  252   $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 
  281   #########################
  282 
  283   $context = $main::context{'LimitedComplex-cartesian'} = $main::context{LimitedComplex}->copy;
  284   $context->flags->set(complex_format => 'cartesian');
  285 
  286   #########################
  287 
  288   $context = $main::context{'LimitedComplex-polar'} = $main::context{LimitedComplex}->copy;
  289   $context->flags->set(complex_format => 'polar');
  290 
  291   #########################
  292 
  293   $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 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9