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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 6217 - (download) (as text) (annotate)
Wed Mar 31 21:45:42 2010 UTC (9 years, 10 months ago) by dpvc
File size: 10334 byte(s)
Remove unneeded parent class

    1 ################################################################################
    2 # WeBWorK Online Homework Delivery System
    3 # Copyright  2000-2010 The WeBWorK Project, http://openwebwork.sf.net/
    4 # $CVSHeader: pg/macros/contextPolynomialFactors.pl,v 1.1 2010/03/31 21:01:14 dpvc Exp $
    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 contextPolynomialFactors.pl - Allow only entry of polynomials, and
   20                               their products and powers
   21 
   22 =head1 DESCRIPTION
   23 
   24 Implements a context in which students can only enter products and
   25 powers of polynomials
   26 
   27 Select the context using:
   28 
   29   Context("PolynomialFactors");
   30 
   31 If you set the "singlePowers" flag, then only one monomial of each
   32 degree can be included in each factor polynomial:
   33 
   34   Context("PolynomialFactors")->flags->set(singlePowers=>1);
   35 
   36 If you set the "singleFactors" flag, then factors can not be repeated.
   37 For example,
   38 
   39   Context("PolynomialFactors")->flags->set(singleFactors=>1);
   40   Formula("(x+1)^2*(x+1)");
   41 
   42 will generate an error indicating that factors can appear only once.
   43 Note, however, that this only catches factors that appear exactly the
   44 same in both cases, so (x+1)*(1+x) would be allowed, as would
   45 -(x-1)*(1-x).  Note also that there is no check for whether the
   46 factors are irreducible, so (x^2-1)*(x+1)*(x-1) would be allowed.
   47 Also, there is no check for whether constants have been factored out,
   48 so 3*(x+1)*(3x+3) would be allowed.  This still needs more work to
   49 make it very useful.
   50 
   51 There are two additional flags that control whether division by a
   52 constant or raising to a power are allowed to be performed on a
   53 product or factors or only on a single factor at at time.  These are
   54 strictDivision and strictPowers.  By default, strictDivisions is 0, so
   55 (x*(x+1))/3 is allowed, while strictPowers is 1, so (x*(x+1))^3 is not
   56 (it must be written x^3*(x+1)^3).
   57 
   58 Finally, there is also a strict limited context that does not allow
   59 operations even within the coefficients.  Select it using:
   60 
   61   Context("PolynomialFactors-Strict");
   62 
   63 In addition to disallowing operations within the coefficients, this
   64 context does not reduce constant operations (since they are not
   65 allowed), and sets the singlePowers, singleFactors, strictDivision,
   66 and stricPowers flags automatically.  In addition, it disables all the
   67 functions, though they can be re-enabled, if needed.
   68 
   69 =cut
   70 
   71 ##################################################
   72 
   73 loadMacros(
   74   "MathObjects.pl",
   75   "contextLimitedPolynomial.pl"
   76 );
   77 
   78 sub _contextPolynomialFactors_init {PolynomialFactors::Init()}
   79 
   80 ##############################################
   81 
   82 package PolynomialFactors::BOP::add;
   83 our @ISA = qw(LimitedPolynomial::BOP::add);
   84 
   85 sub checkPolynomial {
   86   my $self = shift;
   87   my ($l,$r) = ($self->{lop},$self->{rop});
   88   $self->Error("Addition is allowed only between monomials")
   89     if $r->{isPoly} || ($l->{isPoly} && $l->{isPoly} > 2);
   90   $self->checkPowers;
   91 }
   92 
   93 ##############################################
   94 
   95 package PolynomialFactors::BOP::subtract;
   96 our @ISA = qw(LimitedPolynomial::BOP::subtract);
   97 
   98 sub checkPolynomial {
   99   my $self = shift;
  100   my ($l,$r) = ($self->{lop},$self->{rop});
  101   $self->Error("Subtraction is allowed only between monomials")
  102     if $r->{isPoly} || ($l->{isPoly} && $l->{isPoly} > 2);
  103   $self->checkPowers;
  104 }
  105 
  106 ##############################################
  107 
  108 package PolynomialFactors::BOP::multiply;
  109 our @ISA = qw(LimitedPolynomial::BOP::multiply);
  110 
  111 sub checkPolynomial {
  112   my $self = shift;
  113   my ($l,$r) = ($self->{lop},$self->{rop});
  114   my $lOK = (LimitedPolynomial::isConstant($l) || $l->{isPower} ||
  115        $l->class eq 'Variable' || ($l->{isPoly} && $l->{isPoly} == 2));
  116   my $rOK = ($r->{isPower} || $r->class eq 'Variable');
  117   return $self->checkExponents if $lOK and $rOK;
  118   $self->Error("Coefficients must come before variables or factors")
  119     if LimitedPolynomial::isConstant($r) && ($l->{isPower} || $l->class eq 'Variable');
  120   if ($l->{isPoly} || $r->{isPoly}) {
  121     PolynomialFactors::markFactor($l);
  122     PolynomialFactors::markFactor($r);
  123     return $self->checkFactors($l,$r);
  124   }
  125   return 1;
  126 }
  127 
  128 sub checkFactors {
  129   my $self = shift; my ($l,$r) = @_;
  130   my $single = $self->context->flag("singleFactors");
  131   $self->{factors} = $l->{factors}; delete $l->{factors};
  132   foreach my $factor (keys %{$r->{factors}}) {
  133     if ($single && $self->{factors}{$factor}) {
  134       $self->Error("Each factor can appear only once (combine like factors)") unless $factor eq "0";
  135       $self->Error("Only one constant coefficient or negation is allowed (combine them)");
  136     }
  137     $self->{factors}{$factor} = 1;
  138   }
  139   $self->{isPoly} = 4; # product of factors
  140   return 1;
  141 }
  142 
  143 sub checkStrict {
  144   my $self = shift;
  145   $self->Error("You can only use '%s' between coefficents and variables or between factors",$self->{bop});
  146 }
  147 
  148 ##############################################
  149 
  150 package PolynomialFactors::BOP::divide;
  151 our @ISA = qw(LimitedPolynomial::BOP::divide);
  152 
  153 sub checkPolynomial {
  154   my $self = shift;
  155   my ($l,$r) = ($self->{lop},$self->{rop});
  156   $self->Error("In a polynomial, you can only divide by numbers")
  157     unless LimitedPolynomial::isConstant($r);
  158   if ($l->{isPoly} && $l->{isPoly} != 2) {
  159     $self->Error("You can only divide a single term or factor by a number")
  160       if $l->{isPoly} == 3 || ($self->context->flag("strictDivision") && $self->{isPoly} != 1);
  161     PolynomialFactors::markFactor($l);
  162     $self->Error("Only one constant multiple or fraction is allowed (combine them)")
  163       if $l->{factors}{0} && $self->context->flag("singleFactors");
  164     $self->{factors} = $l->{factors}; delete $l->{factors};
  165     $self->{factors}{0} = 1; # mark as constant multiple;
  166     $self->{isPoly} = 3;  # factor over a number
  167   } else {
  168     $self->{isPoly} = $l->{isPoly};
  169     $self->{powers} = $l->{powers}; delete $l->{powers};
  170     $self->{exponents} = $l->{exponents}; delete $l->{exponents};
  171   }
  172   return 1;
  173 }
  174 
  175 ##############################################
  176 
  177 package PolynomialFactors::BOP::power;
  178 our @ISA = qw(LimitedPolynomial::BOP::power);
  179 
  180 sub checkPolynomial {
  181   my $self = shift;
  182   my ($l,$r) = ($self->{lop},$self->{rop});
  183   $self->Error("Exponents must be constant in a polynomial")
  184     unless LimitedPolynomial::isConstant($r);
  185   my $n = Parser::Evaluate($r);
  186   $r->Error($$Value::context->{error}{message}) if $$Value::context->{error}{flag};
  187   $n = $n->value;
  188   $self->Error("Exponents must be positive integers in a polynomial")
  189     unless $n > 0 && $n == int($n);
  190   if ($l->{isPoly}) {
  191     $self->Error("You can only raise a single term or factor to a power")
  192       if $l->{isPoly} > 2 && $self->context->flag("strictPowers");
  193     PolynomialFactors::markFactor($l);
  194     $self->{factors} = $l->{factors}; delete $l->{factors};
  195     $self->{isPoly} = 5; # factor to a power
  196   } else {
  197     LimitedPolynomial::markPowers($l);
  198     $self->{exponents} = $l->{exponents}; delete $l->{exponents};
  199     foreach my $i (@{$self->{exponents}}) {$i = $n if $i}
  200     $self->{isPower} = 1;
  201   }
  202   return 1;
  203 }
  204 
  205 sub checkStrict {
  206   my $self = shift;
  207   $self->Error("You can only use powers of a variable or factor");
  208 }
  209 
  210 ##############################################
  211 
  212 package PolynomialFactors::UOP::minus;
  213 our @ISA = qw(LimitedPolynomial::UOP::minus);
  214 
  215 sub checkPolynomial {
  216   my $self = shift; my $op = $self->{op};
  217   if ($op->{isPoly} && $self->context->flag("singleFactors")) {
  218     $self->Error("Double negatives are not allowed") if $op->{isPoly} == 2;
  219     $self->Error("Only one factor or constant can be negated")
  220       if $op->{isPoly} != 1 && $op->{isPoly} != 5;
  221   }
  222   PolynomialFactors::markFactor($op);
  223   $self->{factors} = $op->{factors}; delete $op->{factors};
  224   $self->{factors}{0} = 1; # mark as constant multiple
  225   return 1;
  226 }
  227 
  228 ##############################################
  229 
  230 package PolynomialFactors;
  231 our @ISA = ('LimitedPolynomal');
  232 
  233 sub markFactor {
  234   my $self = shift;
  235   return if $self->{factors};
  236   $self->{factors} = {};
  237   if ($self->class eq 'Variable') {
  238     $self->{factors}{$self->{name}} = 1;
  239   } elsif ($self->class eq 'Number') {
  240     $self->{factors}{0} = 1;
  241   } elsif ($self->{isPoly} && $self->{isPoly} == 1) {
  242     $self->{factors}{$self->string} = 1;
  243   } elsif ($self->{isPower}) {
  244     $self->{factors}{$self->{lop}->string} = 1;
  245   }
  246 }
  247 
  248 sub Init {
  249   #
  250   #  Build the new context that calls the
  251   #  above classes rather than the usual ones
  252   #
  253 
  254   my $context = $main::context{PolynomialFactors} = Parser::Context->getCopy("LimitedPolynomial");
  255   $context->{name} = "PolynomialFactors";
  256   $context->operators->set(
  257      '+' => {class => 'PolynomialFactors::BOP::add'},
  258      '-' => {class => 'PolynomialFactors::BOP::subtract'},
  259      '*' => {class => 'PolynomialFactors::BOP::multiply'},
  260     '* ' => {class => 'PolynomialFactors::BOP::multiply'},
  261     ' *' => {class => 'PolynomialFactors::BOP::multiply'},
  262      ' ' => {class => 'PolynomialFactors::BOP::multiply'},
  263      '/' => {class => 'PolynomialFactors::BOP::divide'},
  264     ' /' => {class => 'PolynomialFactors::BOP::divide'},
  265     '/ ' => {class => 'PolynomialFactors::BOP::divide'},
  266      '^' => {class => 'PolynomialFactors::BOP::power'},
  267     '**' => {class => 'PolynomialFactors::BOP::power'},
  268     'u-' => {class => 'PolynomialFactors::UOP::minus'},
  269   );
  270   $context->flags->set(strictPowers => 1);
  271 
  272   #
  273   #  A context where coefficients can't include operations
  274   #
  275   $context = $main::context{"PolynomialFactors-Strict"} = $context->copy;
  276   $context->flags->set(
  277     strictCoefficients => 1, strictDivision => 1,
  278     singlePowers => 1, singleFactors => 1,
  279     reduceConstants => 0,
  280   );
  281   $context->functions->disable("All");  # can be re-enabled if needed
  282 
  283   main::Context("PolynomialFactors");  ### FIXME:  probably should require author to set this explicitly
  284 }
  285 
  286 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9