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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 5556 - (download) (as text) (annotate)
Thu Oct 4 16:40:49 2007 UTC (12 years, 3 months ago) by sh002i
File size: 10657 byte(s)
added standard copyright/license header

    1 ################################################################################
    2 # WeBWorK Online Homework Delivery System
    3 # Copyright  2000-2007 The WeBWorK Project, http://openwebwork.sf.net/
    4 # $CVSHeader: webwork2/lib/WeBWorK.pm,v 1.100 2007/08/13 22:59:53 sh002i 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 contextScientificNotation.pl - Allows entry of scientific notation.
   20 
   21 =head1 DESCRIPTION
   22 
   23 This file implements a context in which students can enter
   24 answers in scientific notation.  It tries hard to report
   25 useful error messages when the student's answer is not
   26 in the proper format, and it also allows you to control
   27 how many decimal digits they are allowed/required to
   28 enter, and how many the system will display.
   29 
   30 This probably should be called LimitedScientificNotation
   31 since it does not allow any operations other than the ones
   32 needed in Scientific notation.  In the future it may be
   33 renamed if we produce a computational scientific notation
   34 context.
   35 
   36 To use this context, add
   37 
   38   loadMacros("contextScientificNotation.pl");
   39 
   40 to the top of your problem file, and then use
   41 
   42   Context("ScientificNotation");
   43 
   44 to select the contxt and make it active.  You can create
   45 values in scientific notation in two ways:
   46 
   47   $n1 = Compute("1.23 x 10^3");
   48 
   49 or
   50 
   51   $n2 = ScientificNotation(1.23 * 10**3);
   52 
   53 (or even $n2 = ScientificNotation(1230), and it will be converted).
   54 
   55 You can control how many digits are displayed by setting the
   56 snDigits flag in the context.  For example,
   57 
   58   Context()->flags->set(snDigits=>2);
   59 
   60 sets the context to display at most 2 digits.  The default is 6.
   61 By default, trailing zeros are removed, but you can ask that
   62 they be retained by issuing the command
   63 
   64   Context()->flags->set(snTrimZeros=>0);
   65 
   66 It is also possible to specify how many decimal digits the
   67 student must enter.  For example,
   68 
   69   Context()->flags->set(snMinDigits=>3);
   70 
   71 would require the student to enter at least 3 digits past
   72 the decimal place (for a total of 4 significant digits,
   73 including the one to the left of the decimal).  The default
   74 is 1 digit beyond the decimal.  A value of 0 means that
   75 a decimal point and decimal values is optional.
   76 
   77 Similarly,
   78 
   79   Context()->flags->set(snMaxDigits=>6);
   80 
   81 sets the maximum number to 6, so the student can't enter
   82 more than that.  Setting this to 0 means no decimal places
   83 are allowed, effectively meaning students can only enter
   84 the numbers 0 through 9 (times a power of 10).  Setting
   85 this to a negative number means that there is no upper
   86 limit on the number of digits the student may enter (this
   87 is the default).
   88 
   89 As an example, in order to force a fixed precision of
   90 three digits of precision, use
   91 
   92   Context()->flags->set(
   93     snDigits => 3,
   94     snTrimZeros => 0,
   95     snMinDigits => 3,
   96     snMaxDigits => 3,
   97   );
   98 
   99 Note that if you restrict the number of digits, you may
  100 need to adjust the tolerance values since the student
  101 may not be allowed to enter a more precise answer.  In
  102 the example above, it would be appropriate to set the
  103 tolerance to .0001 and the tolType to "relative" in
  104 order to require the answers to be correct to the three
  105 digits that are shown.
  106 
  107 =cut
  108 
  109 loadMacros("MathObjects.pl");
  110 
  111 sub _contextScientificNotation_init {ScientificNotation::Init()}
  112 
  113 package ScientificNotation;
  114 
  115 #
  116 #  Creates and initializes the ScientificNotation context
  117 #
  118 sub Init {
  119   #
  120   #  Create the Scientific Notation context
  121   #
  122   my $context = $main::context{ScientificNotation} = Parser::Context->getCopy("Numeric");
  123   $context->{name} = "ScientificNotation";
  124 
  125   #
  126   #  Make numbers include the leading + or - and not allow E notation
  127   #
  128   $context->{pattern}{number} = '[-+]?(?:\d+(?:\.\d*)?|\.\d+)';
  129 
  130   #
  131   #  Remove all the stuff we don't need
  132   #
  133   $context->variables->clear;
  134   $context->constants->clear;
  135   $context->parens->clear;
  136   $context->operators->clear;
  137   $context->functions->clear;
  138   $context->strings->clear;
  139 
  140   #
  141   #  Only allow  x  and  ^  operators
  142   #
  143   $context->operators->add(
  144      'x' => {precedence => 3, associativity => 'left', type => 'bin',
  145              string => 'x', TeX => '\times ', perl => '*',
  146              class => 'ScientificNotation::BOP::x'},
  147 
  148      '^' => {precedence => 7, associativity => 'right', type => 'bin',
  149              string => '^', perl => '**',
  150              class => 'ScientificNotation::BOP::power'},
  151 
  152      '**'=> {precedence => 7, associativity => 'right', type => 'bin',
  153              string => '^', perl => '**',
  154              class => 'ScientificNotation::BOP::power'}
  155   );
  156 
  157   #
  158   #  Don't reduce constant values (so 10^2 won't be replaced by 100)
  159   #
  160   $context->flags->set(reduceConstants => 0);
  161   #
  162   #  Flags controlling input and output
  163   #
  164   $context->flags->set(
  165     snDigits => 6,     # number of decimal digits in mantissa for output
  166     snTrimZeros => 1,  # 1 means remove trailing 0's, 0 means leave them
  167     snMinDigits => 1,  # minimum number of decimal digits to require in student input
  168                        #  (0 means no decimal is required)
  169     snMaxDigits => -1, # maximum number of decimals allowed in student input
  170                        #  (negative means no limit)
  171   );
  172 
  173   #
  174   #  Better error message for this case
  175   #
  176   $context->{error}{msg}{"Unexpected character '%s'"} = "'%s' is not allowed in scientific notation";
  177 
  178   #
  179   #  Hook into the Value package lookup mechanism
  180   #
  181   $context->{value}{ScientificNotation} = 'ScientificNotation::Real';
  182   $context->{value}{"Real()"} = 'ScientificNotation::Real';
  183 
  184   #
  185   #  Create the constructor function
  186   #
  187   main::PG_restricted_eval('sub ScientificNotation {Value->Package("ScientificNotation")->new(@_)}');
  188 }
  189 
  190 
  191 ##################################################
  192 #
  193 #  The Scientific Notation multiplication operator
  194 #
  195 package ScientificNotation::BOP::x;
  196 our @ISA = qw(Parser::BOP);
  197 
  198 #
  199 #  Check that the operand types are compatible, and give
  200 #  approrpiate error messages if not.  (We have to work
  201 #  hard to make a good message about the number of
  202 #  decimal digits required.)
  203 #
  204 sub _check {
  205   my $self = shift;
  206   my ($lop,$rop) = ($self->{lop},$self->{rop});
  207   my ($m,$M) = ($self->context->flag("snMinDigits"),$self->context->flag("snMaxDigits"));
  208   $M = $m if $M >= 0 && $M < $m;
  209   my $repeat = ($M < 0 ? "{$m,}" : "{$m,$M}");
  210   my ($digits,$zeros) = ("\\.\\d$repeat","\\.0$repeat");
  211   my $zero = ($m == 0 ? ($M > 0 ? "0.0" : "0") : "0.".("0"x$m));
  212   my $decimals = ($m == $M ? ($m == 0 ? "no digits" : "exactly $m digit".($m == 1 ? "" : "s")) :
  213                  ($M < 0 ?   ($m == 0 ? "" : "at least $m digit".($m == 1 ? "" : "s")) :
  214                              ($m == 0 ? "at most $M digit".($M == 1 ? "" : "s") :
  215                                         "between $m and $M digits")));
  216   $decimals = " and ".$decimals." after it" if $decimals;
  217   $digits = "($digits)?", $zeros = "($zeros)?" if $m == 0;
  218   $self->Error("You must use a power of 10 to the right of 'x' in scientific notation") unless $rop->{isPowerOf10};
  219   $self->Error("You must use a number to the left of 'x' in scientific notation") unless $lop->type eq 'Number';
  220   $self->Error("The number to the left of 'x' must be %s, or have a single, non-zero digit before the decimal%s",
  221                $zero,$decimals) unless $lop->{value_string} =~ m/^[-+]?([1-9]${digits}|0${zeros})$/;
  222   $self->{type} = $Value::type{Number};
  223   $self->{isScientificNotation} = 1;  # mark it so we can tell later on
  224 }
  225 
  226 #
  227 #  Perform the multiplication and return a ScientificNotation object
  228 #
  229 sub _eval {
  230   my ($self,$a,$b) = @_;
  231   $self->Package("ScientificNotation")->make($self->context,$a*$b);
  232 }
  233 
  234 #
  235 #  Use the ScientificNotation MathObject to produce the output formats
  236 #  (if other operators are added back into the context, these will
  237 #   need to be modified to include parens at the appropriate times)
  238 #
  239 sub string {(shift)->eval->string}
  240 sub TeX    {(shift)->eval->TeX}
  241 sub perl   {(shift)->eval->perl}
  242 
  243 ##################################################
  244 #
  245 #  Scientific Notation exponentiation operator
  246 #
  247 package ScientificNotation::BOP::power;
  248 our @ISA = qw(Parser::BOP::power);  # inherit from standard power (TeX method in particular)
  249 
  250 #
  251 #  Check that the operand types are compatible and
  252 #  produce appropriate errors if not
  253 #
  254 sub _check {
  255   my $self = shift;
  256   my ($lop,$rop) = ($self->{lop},$self->{rop});
  257   $self->Error("The base can not have decimal places in scientific notation")
  258     if $lop->{value} == 10 && $lop->{value_string} =~ m/\./;
  259   $self->Error("You must use a power of 10 in scientific notation")
  260     unless $lop->{value_string} eq "10";
  261   $self->Error("The expondent must be an integer in scientific notation")
  262     unless $rop->{value_string} =~ m/^[-+]?\d+$/;
  263   $self->{type} = $Value::type{Number};
  264   $self->{isPowerOf10} = 1;  # mark it so BOP::x above can recognize it
  265 }
  266 
  267 #####################################
  268 #
  269 #  A subclass of Real that handles scientific notation
  270 #
  271 package ScientificNotation::Real;
  272 our @ISA = ("Value::Real");
  273 
  274 #
  275 #  Override these so we can mark ourselves as scientific notation
  276 #
  277 sub new {
  278   my $self = (shift)->SUPER::new(@_);
  279   $self->{isValue} = $self->{isScientificNotation} = 1;
  280   return $self;
  281 }
  282 
  283 sub make {
  284   my $self = (shift)->SUPER::make(@_);
  285   $self->{isValue} = $self->{isScientificNotation} = 1;
  286   return $self;
  287 }
  288 
  289 #
  290 #  Stringify using x notation not E,
  291 #  using the right number of digits, and trimming
  292 #  if requested.
  293 #
  294 sub string {
  295   my $self = shift;
  296   my $digits = $self->getFlag("snDigits");
  297   my $trim = ($self->getFlag("snTrimZeros") ? '0*' : '');
  298   my $r = main::spf($self->value,"%.${digits}e");
  299   $r =~ s/(\d)${trim}e\+?(-?)0*(\d)/$1 x 10^$2$3/i;
  300   return $r;
  301 }
  302 
  303 #
  304 #  Convert x notation to TeX form
  305 #
  306 sub TeX {
  307   my $r = (shift)->string;
  308   $r =~ s/x/\\times /;
  309   $r =~ s/\^(.*)/^{$1}/;
  310   return $r;
  311 }
  312 
  313 #
  314 #  What to call us in error messages
  315 #
  316 sub cmp_class {"Scientific Notation"}
  317 
  318 #
  319 #  Only match against strings and Scientific Notation
  320 #
  321 sub typeMatch {
  322   my $self = shift; my $other = shift; my $ans = shift;
  323   return $other->{isScientificNotation};
  324 }
  325 
  326 #########################################################################
  327 
  328 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9