[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 5354 - (download) (as text) (annotate)
Fri Aug 17 22:46:02 2007 UTC (12 years, 5 months ago) by dpvc
File size: 16175 byte(s)
Make sure the creation o MathObjects internally preserves the context
saved in the object.

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9