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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 5404 - (download) (as text) (annotate)
Wed Aug 22 23:35:41 2007 UTC (12 years, 5 months ago) by dpvc
File size: 17239 byte(s)
Set the cmpDefaults so that you don't get error messages for things
like "x=1 or x=2" (which are not reduced when considered as {1} U {2}).
Also, improve the error messages a bit.

There is a better way to handle this context so that intervals, sets
and unions are distinct from inequalities.  That will clear up the
ambiguity about how to stringify the objects, and will make it easier
for the two types of notation to coincide.

    1 loadMacros("MathObjects.pl");
    2 
    3 sub _contextInequalities_init {Inequalities::Init()}
    4 
    5 =head1 Context("Inequalities"), Context("Inequalities-Only")
    6 
    7  #########################################################################
    8  #
    9  #  Implements contexts that provides for inequalities that produce
   10  #  the cooresponding Interval, Set or Union MathObjects.  There are
   11  #  two such contexts:  Context("Inequalities"), in which both
   12  #  intervals and inequalities are defined, and Context("Inequalities-Only"),
   13  #  which allows only inequalities as a means of producing intervals.
   14  #
   15  #  Usage:    loadMacros("contextInequalities.pl");
   16  #
   17  #            Context("Inequalities");
   18  #            $S1 = Formula("1 < x <= 4");
   19  #            $S2 = Formula("(1,4]");        # either form is OK
   20  #
   21  #            Context("Inequalities-Only");
   22  #            $S1 = Formula("1 < x <= 4");
   23  #            $S2 = Formula("(1,4]");        # generates an error
   24  #
   25  #            $S3 = Formula("x < -2 or x > 2");  # forms a Union
   26  #            $S4 = Formula("x = 1");            # forms a Set
   27  #
   28  #  You can set the "stringifyAsInequalities" flag to 1 to force
   29  #  output from the intervals, sets, and unions created in this
   30  #  context to be output as inequalities rather than their
   31  #  usual Inerval, Set or Union forms.
   32  #
   33  #     Context("Inequalities")->flags->set(stringifyAsInequalities=>1);
   34  #
   35  #  You can also set the "noneWord" flag to specify the string to
   36  #  use when the inequalities specify the empty set.  By default,
   37  #  it is "NONE", but you can change it to other strings.  Be sure
   38  #  that you use a string that is defined in the Context, however,
   39  #  if you expect the student to be able to enter it.  For example
   40  #
   41  #    Context("Inequalities");
   42  #    Context()->constants->add(EmptySet => Set());
   43  #    Context()->flags->set(noneWord=>"EmptySet");
   44  #
   45  #  creates an empty set as a named constant and uses that name.
   46  #
   47 
   48 =cut
   49 
   50 package Inequalities;
   51 
   52 #
   53 #  Sets up the two inequality contexts
   54 #
   55 sub Init {
   56   my $context = $main::context{Inequalities} = Parser::Context->getCopy("Interval");
   57   $context->operators->add(
   58      '<'  => {precedence => .5, associativity => 'left', type => 'bin', string => ' < ',
   59               class => 'Inequalities::BOP::inequality', eval => 'evalLessThan', combine => 1},
   60 
   61      '>'  => {precedence => .5, associativity => 'left', type => 'bin', string => ' > ',
   62               class => 'Inequalities::BOP::inequality', eval => 'evalGreaterThan', combine => 1},
   63 
   64      '<=' => {precedence => .5, associativity => 'left', type => 'bin', string => ' <= ',
   65               class => 'Inequalities::BOP::inequality', eval => 'evalLessThanOrEqualTo', combine => 1},
   66 
   67      '>=' => {precedence => .5, associativity => 'left', type => 'bin', string => ' >= ',
   68               class => 'Inequalities::BOP::inequality', eval => 'evalGreaterThanOrEqualTo', combine => 1},
   69 
   70      '='  => {precedence => .5, associativity => 'left', type => 'bin', string => ' = ',
   71               class => 'Inequalities::BOP::inequality', eval => 'evalEqualTo'},
   72 
   73      '!=' => {precedence => .5, associativity => 'left', type => 'bin', string => ' != ',
   74               class => 'Inequalities::BOP::inequality', eval => 'evalNotEqualTo'},
   75 
   76      'and' => {precedence => .45, associateivity => 'left', type => 'bin', string => " and ",
   77          TeX => '\hbox{ and }', class => 'Inequalities::BOP::and'},
   78 
   79      'or' => {precedence => .4, associateivity => 'left', type => 'bin', string => " or ",
   80         TeX => '\hbox{ or }', class => 'Inequalities::BOP::or'},
   81   );
   82   $context->flags->set(stringifyAsInequalities => 0, noneWord => 'NONE');
   83   $context->{cmpDefaults}{Inequalities} = {reduceSets=>1};
   84   $context->{cmpDefaults}{Interval} = {reduceSets=>1};
   85   $context->{cmpDefaults}{Union} = {reduceSets=>1};
   86   $context->{cmpDefaults}{Set} = {reduceSets=>1};
   87   $context->strings->remove("NONE");
   88   $context->constants->add(NONE=>Value::Set->new());
   89   $context->{parser}{Variable} = "Inequalities::Variable";
   90   $context->{value}{Interval} = "Inequalities::Interval";
   91   $context->{value}{Union} = "Inequalities::Union";
   92   $context->{value}{Set} = "Inequalities::Set";
   93 
   94   #
   95   #  Disable interval notation in Context("Inequalities-Only");
   96   #
   97   $context = $main::context{"Inequalities-Only"} = $context->copy;
   98   $context->parens->remove('(','[','{');
   99   $context->parens->redefine('(',from=>"Numeric");
  100   $context->parens->redefine('[',from=>"Numeric");
  101   $context->parens->redefine('{',from=>"Numeric");
  102   $context->parens->set(
  103     '(' => {formInterval=>0},
  104     '[' => {formInterval=>0}
  105   );
  106   $context->lists->set(List => {class => 'Inequalities::List::List'});
  107   $context->operators->remove('U');
  108   $context->constants->remove('R');
  109   return;
  110 }
  111 
  112 
  113 ##################################################
  114 #
  115 #  General BOP that handles the inequalities.
  116 #  The difference comes in the _eval() method,
  117 #  which tells what each computes.
  118 #
  119 package Inequalities::BOP::inequality;
  120 our @ISA = ("Parser::BOP");
  121 
  122 #
  123 #  Check that the inequality is formed between a variable and a number,
  124 #  or between a number and another compatable inequality.  Otherwise,
  125 #  give an error.
  126 #
  127 #  varPos and numPos tell which of lop or rop is the variable and which
  128 #  the number.  varName is the variable involved in the inequality.
  129 #
  130 sub _check {
  131   my $self = shift;
  132   $self->{type} = Value::Type("Interval",2);
  133   $self->{isInequality} = 1;
  134   ($self->{varPos},$self->{numPos}) =
  135     ($self->{lop}->class eq 'Variable' || $self->{lop}{isInequality} ? ('lop','rop') : ('rop','lop'));
  136   my ($v,$n) = ($self->{$self->{varPos}},$self->{$self->{numPos}});
  137   if ($n->isNumber && $n->{isConstant}) {
  138     if ($v->class eq 'Variable') {
  139       $self->{varName} = $v->{name};
  140       delete $self->{equation}{variables}{$v->{name}} if $v->{isNew};
  141       $self->{$self->{varPos}} = Inequalities::DummyVariable->new($self->{equation},$v->{name},$v->{ref});
  142       return;
  143     }
  144     if ($self->{def}{combine} && $v->{isInequality}) {
  145       my $bop = substr($self->{bop},0,1); my $ebop = $bop."=";
  146       if (($v->{bop} eq $bop || $v->{bop} eq $ebop) && $v->{varPos} eq $self->{numPos}) {
  147   $self->{varName} = $v->{varName};
  148   return;
  149       }
  150     }
  151   }
  152   $self->Error("'%s' should have a variable on one side and a number on the other",$self->{bop})
  153     unless $v->{isInequality} && $v->{varPos} eq $self->{numPos};
  154   $self->Error("'%s' can't be combined with '%s'",$v->{bop},$self->{bop});
  155 }
  156 
  157 #
  158 #  Generate the interval for the given type of inequality.
  159 #  If it is a combined inequality, intersect with the other
  160 #  one to get the final set.
  161 #
  162 sub _eval {
  163   my $self = shift; my ($a,$b) = @_;
  164   my $eval = $self->{def}{eval};
  165   my $I = $self->$eval(@_);
  166   return $I->intersect($a) if Value::isValue($a) && $a->type eq 'Interval';
  167   return $I->intersect($b) if Value::isValue($b) && $b->type eq 'Interval';
  168   return $I;
  169 }
  170 
  171 sub evalLessThan {
  172   my ($self,$a,$b) = @_; my $context = $self->context;
  173   my $I = Value::Infinity->new($context);
  174   return $self->Package("Interval")->new($context,'(',-$I,$b,')') if $self->{varPos} eq 'lop';
  175   return $self->Package("Interval")->new($context,'(',$a,$I,')');
  176 }
  177 
  178 sub evalGreaterThan {
  179   my ($self,$a,$b) = @_; my $context = $self->context;
  180   my $I = Value::Infinity->new;
  181   return $self->Package("Interval")->new($context,'(',$b,$I,')') if $self->{varPos} eq 'lop';
  182   return $self->Package("Interval")->new($context,'(',-$I,$a,')');
  183 }
  184 
  185 sub evalLessThanOrEqualTo {
  186   my ($self,$a,$b) = @_; my $context = $self->context;
  187   my $I = Value::Infinity->new;
  188   return $self->Package("Interval")->new($context,'(',-$I,$b,']') if $self->{varPos} eq 'lop';
  189   return $self->Package("Interval")->new($context,'[',$a,$I,')');
  190 }
  191 
  192 sub evalGreaterThanOrEqualTo {
  193   my ($self,$a,$b) = @_; my $context = $self->context;
  194   my $I = Value::Infinity->new;
  195   return $self->Package("Interval")->new($context,'[',$b,$I,')') if $self->{varPos} eq 'lop';
  196   return $self->Package("Interval")->new($context,'(',-$I,$a,']');
  197 }
  198 
  199 sub evalEqualTo {
  200   my ($self,$a,$b) = @_; my $context = $self->context;
  201   my $x = ($self->{varPos} eq 'lop' ? $b : $a);
  202   return $self->Package("Set")->new($context,$x);
  203 }
  204 
  205 sub evalNotEqualTo {
  206   my ($self,$a,$b) = @_; my $context = $self->context;
  207   my $x = ($self->{varPos} eq 'lop' ? $b : $a);
  208   my $I = Value::Infinity->new;
  209   return $self->Package("Union")->new($context,
  210             $self->Package("Interval")->new($context,'(',-$I,$x,')'),
  211             $self->Package("Interval")->new($context,'(',$x,$I,')')
  212          );
  213 }
  214 
  215 #
  216 #  Inequalities have dummy variables that are not really
  217 #  variables of a formula.
  218 
  219 sub getVariables {{}}
  220 
  221 #
  222 #  Avoid unwanted parentheses from the standard routines.
  223 #
  224 sub string {
  225   my ($self,$precedence) = @_;
  226   my $string; my $bop = $self->{def};
  227 
  228   $string = $self->{lop}->string($bop->{precedence}).
  229             $bop->{string}.
  230             $self->{rop}->string($bop->{precedence});
  231 
  232   return $string;
  233 }
  234 
  235 sub TeX {
  236   my ($self,$precedence) = @_;
  237   my $TeX; my $bop = $self->{def};
  238 
  239   $TeX = $self->{lop}->TeX($bop->{precedence}).
  240          (defined($bop->{TeX}) ? $bop->{TeX} : $bop->{string}) .
  241          $self->{rop}->TeX($bop->{precedence});
  242 
  243   return $TeX;
  244 }
  245 
  246 ##################################################
  247 #
  248 #  Implements the "and" operation as set intersection
  249 #
  250 package Inequalities::BOP::and;
  251 our @ISA = ("Parser::BOP");
  252 
  253 sub _check {
  254   my $self = shift;
  255   $self->Error("The operands of '%s' must be Intervals, Sets or Unions")
  256     unless $self->{lop}->isSetOfReals && $self->{rop}->isSetOfReals;
  257   $self->{type} = Value::Type("Interval",2);
  258   $self->{varName} = $self->{lop}{varName} || $self->{rop}{varName};
  259 }
  260 
  261 sub _eval {$_[1]->intersect($_[2])}
  262 
  263 ##################################################
  264 #
  265 #  Implements the "or" operation as set union
  266 #
  267 package Inequalities::BOP::or;
  268 our @ISA = ("Parser::BOP");
  269 
  270 sub _check {
  271   my $self = shift;
  272   $self->Error("The operands of '%s' must be Intervals, Sets or Unions")
  273     unless $self->{lop}->isSetOfReals && $self->{rop}->isSetOfReals;
  274   $self->{type} = Value::Type("Interval",2);
  275   $self->{varName} = $self->{lop}{varName} || $self->{rop}{varName};
  276 }
  277 
  278 sub _eval {$_[1] + $_[2]}
  279 
  280 ##################################################
  281 #
  282 #  Subclass of Parser::Variable that records whether
  283 #  this variable has already been seen in the formula
  284 #  (so that it can be removed from the formula's
  285 #  variable list when used in an inequality.)
  286 #
  287 package Inequalities::Variable;
  288 our @ISA = ("Parser::Variable");
  289 
  290 sub new {
  291   my $self = shift; my $equation = shift; my $name = shift;
  292   my $isNew = !defined $equation->{variables}{$name};
  293   my $n = $self->SUPER::new($equation,$name,@_);
  294   $n->{isNew} = $isNew;
  295   return $n;
  296 }
  297 
  298 ##################################################
  299 #
  300 #  A special class usd for the variables in
  301 #  inequalities, since they are not really
  302 #  variables for the formula.  (They don't need
  303 #  to be subtituted or given values when the
  304 #  formula is evaluated, and so on.)  These are
  305 #  really just placeholders, here.
  306 #
  307 package Inequalities::DummyVariable;
  308 our @ISA = ("Parser::Item");
  309 
  310 sub new {
  311   my $self = shift; my $class = ref($self) || $self;
  312   my ($equation,$name,$ref) = @_;
  313   my $def = $equation->{context}{variables}{$name};
  314   bless {name => $name, ref => $ref, def => $def, equation => $equation}, $class;
  315 }
  316 
  317 sub eval {shift};
  318 
  319 sub string {(shift)->{name}}
  320 
  321 sub TeX {
  322   my $self = shift; my $name = $self->{name};
  323   return $self->{def}{TeX} if defined $self->{def}{TeX};
  324   $name = $1.'_{'.$2.'}' if ($name =~ m/^([^_]+)_?(\d+)$/);
  325   return $name;
  326 }
  327 
  328 sub perl {
  329   my $self = shift;
  330   return $self->{def}{perl} if defined $self->{def}{perl};
  331   return '$'.$self->{name};
  332 }
  333 
  334 ##################################################
  335 #
  336 #  For the Inequalities-Only context, we make lists
  337 #  that report errors, so that students MUST produce
  338 #  their intervals via inequalities.
  339 #
  340 package Inequalities::List::List;
  341 our @ISA = ("Parser::List::List");
  342 
  343 sub _check {
  344   my $self = shift;
  345   $self->SUPER::_check(@_);
  346   $self->Error("You are not allowed to use intervals or sets in this context") if $self->{open};
  347 }
  348 
  349 ##################################################
  350 #
  351 #  Override the string and TeX methods
  352 #  so that we can strinfigy as inequalities
  353 #  rather than intervals.
  354 #
  355 package Inequalities::Interval;
  356 our @ISA = ("Value::Interval");
  357 
  358 sub new {
  359   my $self = shift; $self = $self->SUPER::new(@_);
  360   $self->{isValue} = 1;
  361   return $self;
  362 }
  363 
  364 sub make {
  365   my $self = shift; $self = $self->SUPER::make(@_);
  366   $self->{isValue} = 1;
  367   return $self;
  368 }
  369 
  370 sub string {
  371   my $self = shift;
  372   return $self->SUPER::string(@_) unless $self->getFlag('stringifyAsInequalities');
  373   my ($a,$b,$open,$close) = $self->value;
  374   my $x = ($self->context->variables->names)[0];
  375   $x = $context->{variables}{$x}{string} if defined $context->{variables}{$x}{string};
  376   my $left  = ($open  eq '(' ? ' < ' : ' <= ');
  377   my $right = ($close eq ')' ? ' < ' : ' <= ');
  378   my $inequality = "";
  379   $inequality .= $a->string.$left unless $self->{leftInfinite};
  380   $inequality .= $x;
  381   $inequality .= $right.$b->string unless $self->{rightInfinite};
  382   $inequality = "-infinity < $x < infinity" if $inequality eq $x;
  383   return $inequality;
  384 }
  385 
  386 sub TeX {
  387   my $self = shift;
  388   return $self->SUPER::TeX(@_) unless $self->getFlag('stringifyAsInequalities');
  389   my ($a,$b,$open,$close) = $self->value;
  390   my $context = $self->context;
  391   my $x = ($context->variables->names)[0];
  392   $x = $context->{variables}{$x}{TeX} if defined $context->{variables}{$x}{TeX};
  393   $x =~ s/^([^_]+)_?(\d+)$/$1_{$2}/;
  394   my $left  = ($open  eq '(' ? ' < ' : ' <= ');
  395   my $right = ($close eq ')' ? ' < ' : ' <= ');
  396   my $inequality = "";
  397   $inequality .= $a->string.$left unless $self->{leftInfinite};
  398   $inequality .= $x;
  399   $inequality .= $right.$b->string unless $self->{rightInfinite};
  400   $inequality = "-\\infty < $x < \\infty " if $inequality eq $x;
  401   return $inequality;
  402 }
  403 
  404 sub cmp_class {"an Inequality"}
  405 
  406 ##################################################
  407 #
  408 #  Override the string and TeX methods
  409 #  so that we can strinfigy as inequalities
  410 #  rather than unions.
  411 #
  412 package Inequalities::Union;
  413 our @ISA = ("Value::Union");
  414 
  415 sub new {
  416   my $self = shift; $self = $self->SUPER::new(@_);
  417   $self->{isValue} = 1;
  418   return $self;
  419 }
  420 
  421 sub make {
  422   my $self = shift; $self = $self->SUPER::make(@_);
  423   $self->{isValue} = 1;
  424   return $self;
  425 }
  426 
  427 sub string {
  428   my $self = shift;
  429   return $self->SUPER::string(@_) unless $self->getFlag('stringifyAsInequality');
  430   my $equation = shift; shift; shift; my $prec = shift;
  431   my $op = ($equation->{context} || $self->context)->{operators}{'or'};
  432   my @intervals = ();
  433   foreach my $x (@{$self->data}) {
  434     $x->{format} = $self->{format} if defined $self->{format};
  435     push(@intervals,$x->string($equation))
  436   }
  437   my $string = join($op->{string} || ' or ',@intervals);
  438   $string = '('.$string.')' if $prec > ($op->{precedence} || 1.5);
  439   return $string;
  440 }
  441 
  442 sub TeX {
  443   my $self = shift;
  444   return $self->SUPER::TeX(@_) unless $self->getFlag('stringifyAsInequality');
  445   my $equation = shift; shift; shift; my $prec = shift;
  446   my $op = ($equation->{context} || $self->context)->{operators}{'or'};
  447   my @intervals = ();
  448   foreach my $x (@{$self->data}) {push(@intervals,$x->TeX($equation))}
  449   my $TeX = join($op->{TeX} || $op->{string} || ' or ',@intervals);
  450   $TeX = '\left('.$TeX.'\right)' if $prec > ($op->{precedence} || 1.5);
  451   return $TeX;
  452 }
  453 
  454 sub cmp_class {"an Inequality"}
  455 
  456 ##################################################
  457 #
  458 #  Override the string and TeX methods
  459 #  so that we can strinfigy as inequalities
  460 #  rather than sets.
  461 #
  462 package Inequalities::Set;
  463 our @ISA = ("Value::Set");
  464 
  465 sub new {
  466   my $self = shift; $self = $self->SUPER::new(@_);
  467   $self->{isValue} = 1;
  468   return $self;
  469 }
  470 
  471 sub make {
  472   my $self = shift; $self = $self->SUPER::make(@_);
  473   $self->{isValue} = 1;
  474   return $self;
  475 }
  476 
  477 sub string {
  478   my $self = shift;  my $equation = shift;
  479   return $self->SUPER::string($equation,@_) unless $self->getFlag('stringifyAsInequality');
  480   my $x = ($self->context->variables->names)[0];
  481   $x = $context->{variables}{$x}{string} if defined $context->{variables}{$x}{string};
  482   my @coords = ();
  483   foreach my $a (@{$self->data}) {
  484     if (Value::isValue($a)) {
  485       $a->{format} = $self->{format} if defined $self->{format};
  486       push(@coords,$x.' = '.$a->string($equation));
  487     } else {
  488       push(@coords,$x.' = '.$a);
  489     }
  490   }
  491   return $self->getFlag('noneWord') unless scalar(@coords);
  492   return join(" or ",@coords);
  493 }
  494 
  495 sub TeX {
  496   my $self = shift;  my $equation = shift;
  497   return $self->SUPER::TeX($equation,@_) unless $self->getFlag('stringifyAsInequality');
  498   my $x = ($self->context->variables->names)[0];
  499   $x = $context->{variables}{$x}{TeX} if defined $context->{variables}{$x}{TeX};
  500   $x =~ s/^([^_]+)_?(\d+)$/$1_{$2}/;
  501   my @coords = ();
  502   foreach my $a (@{$self->data}) {
  503     if (Value::isValue($a)) {
  504       $a->{format} = $self->{format} if defined $self->{format};
  505       push(@coords,$x.' = '.$a->TeX($equation));
  506     } else {
  507       push(@coords,$x.' = '.$a);
  508     }
  509   }
  510   return '\hbox{'.$self->getFlag('noneWord').'}' unless scalar(@coords);
  511   return join(" or ",@coords);
  512 }
  513 
  514 sub cmp_class {"an Equality"}
  515 
  516 ##################################################
  517 
  518 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9