[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 5373 - (download) (as text) (annotate)
Sun Aug 19 02:01:57 2007 UTC (12 years, 3 months ago) by dpvc
File size: 9953 byte(s)
Normalized comments and headers to that they will format their POD
documentation properly.  (I know that the POD processing was supposed
to strip off the initial #, but that doesn't seem to happen, so I've
added a space throughout.)

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9