[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 5551 - (download) (as text) (annotate)
Tue Oct 2 20:48:05 2007 UTC (12 years, 2 months ago) by sh002i
File size: 9430 byte(s)
improved formatting for docs -- these were in pod sections but were all
formatted as verbatim sections, and i moved them into normal paragraphs,
lists, etc. should make things more readable from the web.

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9