[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 5327 - (download) (as text) (annotate)
Wed Aug 15 03:56:11 2007 UTC (12 years, 6 months ago) by dpvc
File size: 15877 byte(s)
This file defines contexts in which intervals can (or must) be
entered as inequalities.  The contexts include "and" and "or" for
combining inequalities, and you can force intervals to be shown as
inequalities rather than their usual form.  See the comments in the
file for additional details.

    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) = @_;
  170   my $I = Value::Infinity->new;
  171   return Value->Package("Interval")->new('(',-$I,$b,')') if $self->{varPos} eq 'lop';
  172   return Value->Package("Interval")->new('(',$a,$I,')');
  173 }
  174 
  175 sub evalGreaterThan {
  176   my ($self,$a,$b) = @_;
  177   my $I = Value::Infinity->new;
  178   return Value->Package("Interval")->new('(',$b,$I,')') if $self->{varPos} eq 'lop';
  179   return Value->Package("Interval")->new('(',-$I,$a,')');
  180 }
  181 
  182 sub evalLessThanOrEqualTo {
  183   my ($self,$a,$b) = @_;
  184   my $I = Value::Infinity->new;
  185   return Value->Package("Interval")->new('(',-$I,$b,']') if $self->{varPos} eq 'lop';
  186   return Value->Package("Interval")->new('[',$a,$I,')');
  187 }
  188 
  189 sub evalGreaterThanOrEqualTo {
  190   my ($self,$a,$b) = @_;
  191   my $I = Value::Infinity->new;
  192   return Value->Package("Interval")->new('[',$b,$I,')') if $self->{varPos} eq 'lop';
  193   return Value->Package("Interval")->new('(',-$I,$a,']');
  194 }
  195 
  196 sub evalEqualTo {
  197   my ($self,$a,$b) = @_;
  198   my $x = ($self->{varPos} eq 'lop' ? $b : $a);
  199   return Value->Package("Set")->new($x);
  200 }
  201 
  202 sub evalNotEqualTo {
  203   my ($self,$a,$b) = @_;
  204   my $x = ($self->{varPos} eq 'lop' ? $b : $a);
  205   my $I = Value::Infinity->new;
  206   return Value->Package("Union")->new(
  207             Value->Package("Interval")->new('(',-$I,$x,')'),
  208             Value->Package("Interval")->new('(',$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