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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3371 - (download) (as text) (annotate)
Tue Jul 12 22:39:56 2005 UTC (14 years, 6 months ago) by dpvc
File size: 8751 byte(s)
Make error messages potentially localizable (by making them use
sprintf-style strings rather than variable subtitution).

    1 loadMacros("Parser.pl");
    2 
    3 sub _contextLimitedPolynomial_init {}; # don't load it again
    4 
    5 ##########################################################
    6 #
    7 #  Implements a context in which students can only
    8 #  enter (expanded) polynomials (i.e., sums of multiples
    9 #  of powers of x).
   10 #
   11 #  Select the context using:
   12 #
   13 #      Context("LimitedPolynomial");
   14 #
   15 #  If you set the "singlePowers" flag, then only one monomial of
   16 #  each degree can be included in the polynomial:
   17 #
   18 #      Context("LimitedPolynomial")->flags->set(singlePowers=>1);
   19 #
   20 
   21 #
   22 #  Handle common checking for BOPs
   23 #
   24 package LimitedPolynomial::BOP;
   25 
   26 #
   27 #  Do original check and then if the operands are numbers, its OK.
   28 #  Otherwise, do an operator-specific check for if the polynomial is OK.
   29 #  Otherwise report an error.
   30 #
   31 sub _check {
   32   my $self = shift;
   33   my $super = ref($self); $super =~ s/LimitedPolynomial/Parser/;
   34   &{$super."::_check"}($self);
   35   return if LimitedPolynomial::isConstant($self->{lop}) &&
   36             LimitedPolynomial::isConstant($self->{rop});
   37   return if $self->checkPolynomial;
   38   $self->Error("Your answer doesn't look like a polynomial");
   39 }
   40 
   41 #
   42 #  filled in by subclasses
   43 #
   44 sub checkPolynomial {return 0}
   45 
   46 #
   47 #  Check that the powers of combined monomials are OK
   48 #  and record the new power list
   49 #
   50 sub checkPowers {
   51   my $self = shift;
   52   my ($l,$r) = ($self->{lop},$self->{rop});
   53   my $single = $self->{equation}{context}->flag('singlePowers');
   54   $self->{isPoly} = 1;
   55   $self->{powers} = $l->{powers}? {%{$l->{powers}}} : {};
   56   $r->{powers} = {1=>1} if $r->class eq 'Variable';
   57   return 1 unless $r->{powers};
   58   foreach my $n (keys(%{$r->{powers}})) {
   59     $self->Error("Polynomials can have at most one term of each degree")
   60       if $self->{powers}{$n} && $single;
   61     $self->{powers}{$n} = 1;
   62   }
   63   return 1;
   64 }
   65 
   66 package LimitedPolynomial;
   67 
   68 #
   69 #  Check for a constant expression
   70 #
   71 sub isConstant {
   72   my $self = shift;
   73   return 1 if $self->{isConstant} || $self->class eq 'Constant';
   74   return scalar(keys(%{$self->getVariables})) == 0;
   75 }
   76 
   77 ##############################################
   78 #
   79 #  Now we get the individual replacements for the operators
   80 #  that we don't want to allow.  We inherit everything from
   81 #  the original Parser::BOP class, and just add the
   82 #  polynomial checks here.  Note that checkpolynomial
   83 #  only gets called if at least one of the terms is not
   84 #  a number.
   85 #
   86 
   87 package LimitedPolynomial::BOP::add;
   88 our @ISA = qw(LimitedPolynomial::BOP Parser::BOP::add);
   89 
   90 sub checkPolynomial {
   91   my $self = shift;
   92   my ($l,$r) = ($self->{lop},$self->{rop});
   93   $self->Error("Addition is allowed only between monomials")
   94     if $r->{isPoly};
   95   $self->checkPowers;
   96 }
   97 
   98 ##############################################
   99 
  100 package LimitedPolynomial::BOP::subtract;
  101 our @ISA = qw(LimitedPolynomial::BOP Parser::BOP::subtract);
  102 
  103 sub checkPolynomial {
  104   my $self = shift;
  105   my ($l,$r) = ($self->{lop},$self->{rop});
  106   $self->Error("Subtraction is only allowed between monomials")
  107     if $r->{isPoly};
  108   $self->checkPowers;
  109 }
  110 
  111 ##############################################
  112 
  113 package LimitedPolynomial::BOP::multiply;
  114 our @ISA = qw(LimitedPolynomial::BOP Parser::BOP::multiply);
  115 
  116 sub checkPolynomial {
  117   my $self = shift;
  118   my ($l,$r) = ($self->{lop},$self->{rop});
  119   if (LimitedPolynomial::isConstant($l) && ($r->{isPower} || $r->class eq 'Variable')) {
  120     $r->{powers} = {1=>1} unless $r->{isPower};
  121     $self->{powers} = {%{$r->{powers}}};
  122     return 1;
  123   }
  124   $self->Error("Coefficients must come before variables in a polynomial")
  125     if LimitedPolynomial::isConstant($r) && ($l->{isPower} || $l->class eq 'Variable');
  126   $self->Error("Multiplication can only be used between coefficients and variables");
  127 }
  128 
  129 ##############################################
  130 
  131 package LimitedPolynomial::BOP::divide;
  132 our @ISA = qw(LimitedPolynomial::BOP Parser::BOP::divide);
  133 
  134 sub checkPolynomial {
  135   my $self = shift;
  136   my ($l,$r) = ($self->{lop},$self->{rop});
  137   $self->Error("You can only divide by a number in a polynomial")
  138     unless LimitedPolynomial::isConstant($r);
  139   $self->Error("You can only divide a single monomial by a number")
  140     if $l->{isPoly} && $l->{isPoly} == 1;
  141   $self->{isPoly} = $l->{isPoly};
  142   $self->{powers} = {%{$l->{powers}}} if $l->{powers};
  143   return 1;
  144 }
  145 
  146 ##############################################
  147 
  148 package LimitedPolynomial::BOP::power;
  149 our @ISA = qw(LimitedPolynomial::BOP Parser::BOP::power);
  150 
  151 sub checkPolynomial {
  152   my $self = shift;
  153   my ($l,$r) = ($self->{lop},$self->{rop});
  154   $self->{isPower} = 1;
  155   $self->Error("You can only raise a variable to a power in a polynomial")
  156     unless $l->class eq 'Variable';
  157   $self->Error("Exponents must be constant in a polynomial")
  158     unless LimitedPolynomial::isConstant($r);
  159   my $n = Parser::Evaluate($r);
  160   $r->Error($$Value::context->{error}{message}) if $$Value::context->{error}{flag};
  161   $self->Error("Exponents must be positive integers in a polynomial")
  162     unless $n > 0 && $n == int($n);
  163   $self->{powers} = {$n=>1};
  164   return 1;
  165 }
  166 
  167 ##############################################
  168 ##############################################
  169 #
  170 #  Now we do the same for the unary operators
  171 #
  172 
  173 package LimitedPolynomial::UOP;
  174 
  175 sub _check {
  176   my $self = shift;
  177   my $super = ref($self); $super =~ s/LimitedPolynomial/Parser/;
  178   &{$super."::_check"}($self);
  179   my $op = $self->{op};
  180   return if LimitedPolynomail::isConstant($op);
  181   $self->Error("You can only use '%s' with monomials",$self->{def}{string})
  182     if $op->{isPoly};
  183   $self->{isPoly} = 2;
  184   $self->{powers} = {%{$op->{powers}}} if $op->{powers};
  185 }
  186 
  187 sub checkPolynomial {return 0}
  188 
  189 ##############################################
  190 
  191 package LimitedPolynomial::UOP::plus;
  192 our @ISA = qw(LimitedPolynomial::UOP Parser::UOP::plus);
  193 
  194 ##############################################
  195 
  196 package LimitedPolynomial::UOP::minus;
  197 our @ISA = qw(LimitedPolynomial::UOP Parser::UOP::minus);
  198 
  199 ##############################################
  200 ##############################################
  201 #
  202 #  Don't allow absolute values
  203 #
  204 
  205 package LimitedPolynomial::List::AbsoluteValue;
  206 our @ISA = qw(Parser::List::AbsoluteValue);
  207 
  208 sub _check {
  209   my $self = shift;
  210   $self->SUPER::_check;
  211   return if LimitedPolynomial::isConstant($self->{coords}[0]);
  212   $self->Error("Can't use absolute values in polynomials");
  213 }
  214 
  215 ##############################################
  216 ##############################################
  217 #
  218 #  Only allow numeric function calls
  219 #
  220 
  221 package LimitedPolynomial::Function;
  222 
  223 sub _check {
  224   my $self = shift;
  225   my $super = ref($self); $super =~ s/LimitedPolynomial/Parser/;
  226   &{$super."::_check"}($self);
  227   my $arg = $self->{params}->[0];
  228   return if LimitedPolynomial::isConstant($arg);
  229   $self->Error("Function '%s' can only be used with numbers",$self->{name});
  230 }
  231 
  232 
  233 package LimitedPolynomial::Function::numeric;
  234 our @ISA = qw(LimitedPolynomial::Function Parser::Function::numeric);
  235 
  236 package LimitedPolynomial::Function::trig;
  237 our @ISA = qw(LimitedPolynomial::Function Parser::Function::trig);
  238 
  239 ##############################################
  240 ##############################################
  241 
  242 package main;
  243 
  244 #
  245 #  Now build the new context that calls the
  246 #  above classes rather than the usual ones
  247 #
  248 
  249 $context{LimitedPolynomial} = Context("Numeric");
  250 $context{LimitedPolynomial}->operators->set(
  251    '+' => {class => 'LimitedPolynomial::BOP::add'},
  252    '-' => {class => 'LimitedPolynomial::BOP::subtract'},
  253    '*' => {class => 'LimitedPolynomial::BOP::multiply'},
  254   '* ' => {class => 'LimitedPolynomial::BOP::multiply'},
  255   ' *' => {class => 'LimitedPolynomial::BOP::multiply'},
  256    ' ' => {class => 'LimitedPolynomial::BOP::multiply'},
  257    '/' => {class => 'LimitedPolynomial::BOP::divide'},
  258   ' /' => {class => 'LimitedPolynomial::BOP::divide'},
  259   '/ ' => {class => 'LimitedPolynomial::BOP::divide'},
  260    '^' => {class => 'LimitedPolynomial::BOP::power'},
  261   '**' => {class => 'LimitedPolynomial::BOP::power'},
  262   'u+' => {class => 'LimitedPolynomial::UOP::plus'},
  263   'u-' => {class => 'LimitedPolynomial::UOP::minus'},
  264 );
  265 #
  266 #  Remove these operators and functions
  267 #
  268 $context{LimitedPolynomial}->lists->set(
  269   AbsoluteValue => {class => 'LimitedPolynomial::List::AbsoluteValue'},
  270 );
  271 $context{LimitedPolynomial}->operators->undefine('_','!','U');
  272 $context{LimitedPolynomial}->functions->disable("Hyperbolic","atan2");
  273 #
  274 #  Hook into the numeric and trig functions
  275 #
  276 foreach ('sin','cos','tan','sec','csc','cot',
  277          'asin','acos','atan','asec','acsc','acot') {
  278   $context{LimitedPolynomial}->functions->set(
  279      "$_"=>{class => 'LimitedPolynomial::Function::trig'}
  280   );
  281 }
  282 foreach ('ln','log','log10','exp','sqrt','abs','int','sgn') {
  283   $context{LimitedPolynomial}->functions->set(
  284     "$_"=>{class => 'LimitedPolynomial::Function::numeric'}
  285   );
  286 }
  287 
  288 Context("LimitedPolynomial");

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9