[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 5470 - (download) (as text) (annotate)
Sat Sep 8 21:44:43 2007 UTC (12 years, 5 months ago) by dpvc
File size: 24105 byte(s)
No longer need to change the order of the data, since
Value::Interval->make() now handles the alternative order.

    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 = Compute("1 < x <= 4");
   19  #            $S2 = Inequality("(1,4]");     # force interval to be inequality
   20  #
   21  #            Context("Inequalities-Only");
   22  #            $S1 = Compute("1 < x <= 4");
   23  #            $S2 = Inequality("(1,4]");     # generates an error
   24  #
   25  #            $S3 = Compute("x < -2 or x > 2");  # forms a Union
   26  #            $S4 = Compute("x = 1");            # forms a Set
   27  #
   28  #  You can set the "noneWord" flag to specify the string to
   29  #  use when the inequalities specify the empty set.  By default,
   30  #  it is "NONE", but you can change it to other strings.  Be sure
   31  #  that you use a string that is defined in the Context, however,
   32  #  if you expect the student to be able to enter it.  For example
   33  #
   34  #    Context("Inequalities");
   35  #    Context()->constants->add(EmptySet => Set());
   36  #    Context()->flags->set(noneWord=>"EmptySet");
   37  #
   38  #  creates an empty set as a named constant and uses that name.
   39  #
   40  #  Inequalities and interval notation both can coexist side by
   41  #  side, but you may wish to convert from one to the other.
   42  #  Use Inequality() to convert from an Interval, Set or Union
   43  #  to an Inequality, and use Interval(), Set(), or Union() to
   44  #  convert from an Inequality object to one in interval notation.
   45  #  For example:
   46  #
   47  #    $I0 = Compute("(1,2]");            # the interval (1,2]
   48  #    $I1 = Inequality($I);              # the inequality 1 < x <= 2
   49  #
   50  #    $I0 = Compute("1 < x <= 2");       # the inequality 1 < x <= 2
   51  #    $I1 = Interval($I0);               # the interval (1,2]
   52  #
   53  #  Note that ineqaulities and inervals can be compared and combined
   54  #  regardless of the format, so $I0 == $I1 is true in either example
   55  #  above.
   56  #
   57  ######################################################################
   58 
   59 =cut
   60 
   61 package Inequalities;
   62 
   63 #
   64 #  Sets up the two inequality contexts
   65 #
   66 sub Init {
   67   my $context = $main::context{Inequalities} = Parser::Context->getCopy("Interval");
   68   $context->{name} = "Inequalities";
   69   $context->operators->add(
   70      '<'  => {precedence => .5, associativity => 'left', type => 'bin', string => ' < ',
   71               class => 'Inequalities::BOP::inequality', eval => 'evalLessThan', combine => 1},
   72 
   73      '>'  => {precedence => .5, associativity => 'left', type => 'bin', string => ' > ',
   74               class => 'Inequalities::BOP::inequality', eval => 'evalGreaterThan', combine => 1},
   75 
   76      '<=' => {precedence => .5, associativity => 'left', type => 'bin', string => ' <= ', TeX => '\le ',
   77               class => 'Inequalities::BOP::inequality', eval => 'evalLessThanOrEqualTo', combine => 1},
   78 
   79      '>=' => {precedence => .5, associativity => 'left', type => 'bin', string => ' >= ', TeX => '\ge ',
   80               class => 'Inequalities::BOP::inequality', eval => 'evalGreaterThanOrEqualTo', combine => 1},
   81 
   82      '='  => {precedence => .5, associativity => 'left', type => 'bin', string => ' = ',
   83               class => 'Inequalities::BOP::inequality', eval => 'evalEqualTo'},
   84 
   85      '!=' => {precedence => .5, associativity => 'left', type => 'bin', string => ' != ', TeX => '\ne ',
   86               class => 'Inequalities::BOP::inequality', eval => 'evalNotEqualTo'},
   87 
   88      'and' => {precedence => .45, associateivity => 'left', type => 'bin', string => " and ",
   89          TeX => '\hbox{ and }', class => 'Inequalities::BOP::and'},
   90 
   91      'or' => {precedence => .4, associateivity => 'left', type => 'bin', string => " or ",
   92         TeX => '\hbox{ or }', class => 'Inequalities::BOP::or'},
   93   );
   94   $context->operators->set(
   95      '+' => {class => "Inequalities::BOP::add"},
   96      '-' => {class => "Inequalities::BOP::subtract"},
   97   );
   98   $context->parens->set("(" => {type => "List", formIntervla => ']'});  # trap these later
   99   $context->parens->set("[" => {type => "List", formIntervla => ')'});  # trap these later
  100   $context->strings->remove("NONE");
  101   $context->constants->add(NONE=>Value::Set->new());
  102   $context->flags->set(noneWord => 'NONE');
  103   $context->{parser}{Variable} = "Inequalities::Variable";
  104   $context->{value}{'Interval()'} = "Inequalities::MakeInterval";
  105   $context->{value}{Inequality} = "Inequalities::Inequality";
  106   $context->{value}{InequalityInterval} = "Inequalities::Interval";
  107   $context->{value}{InequalityUnion} = "Inequalities::Union";
  108   $context->{value}{InequalitySet} = "Inequalities::Set";
  109   $context->{value}{List} = "Inequalities::List";
  110   $context->{precedence}{Inequality} = $context->{precedence}{special};
  111   $context->lists->set(List => {class => 'Inequalities::List::List'});
  112 
  113   #
  114   #  Disable interval notation in "Inequalities-Only" context
  115   #
  116   $context = $main::context{"Inequalities-Only"} = $context->copy;
  117   $context->lists->set(
  118     Interval => {class => 'Inequalities::List::notAllowed'},
  119     Set      => {class => 'Inequalities::List::notAllowed'},
  120     Union    => {class => 'Inequalities::List::notAllowed'},
  121   );
  122   $context->operators->set('U' => {class => 'Inequalities::BOP::union'});
  123   $context->constants->remove('R');
  124 
  125   #
  126   #  Define the Inequality() constructor
  127   #
  128   main::PG_restricted_eval('sub Inequality {Value->Package("Inequality")->new(@_)}');
  129 
  130   return;
  131 }
  132 
  133 
  134 ##################################################
  135 #
  136 #  General BOP that handles the inequalities.
  137 #  The difference comes in the _eval() method,
  138 #  which tells what each computes.
  139 #
  140 package Inequalities::BOP::inequality;
  141 our @ISA = ("Parser::BOP");
  142 
  143 #
  144 #  Check that the inequality is formed between a variable and a number,
  145 #  or between a number and another compatible inequality.  Otherwise,
  146 #  give an error.
  147 #
  148 #  varPos and numPos tell which of lop or rop is the variable and which
  149 #  the number.  varName is the variable involved in the inequality.
  150 #
  151 sub _check {
  152   my $self = shift;
  153   $self->{type} = $Value::Type{interval};
  154   $self->{isInequality} = 1;
  155   ($self->{varPos},$self->{numPos}) =
  156     ($self->{lop}->class eq 'Variable' || $self->{lop}{isInequality} ? ('lop','rop') : ('rop','lop'));
  157   my ($v,$n) = ($self->{$self->{varPos}},$self->{$self->{numPos}});
  158   if ($n->isNumber && $n->{isConstant}) {
  159     if ($v->class eq 'Variable') {
  160       $self->{varName} = $v->{name};
  161       delete $self->{equation}{variables}{$v->{name}} if $v->{isNew};
  162       $self->{$self->{varPos}} = Inequalities::DummyVariable->new($self->{equation},$v->{name},$v->{ref});
  163       return;
  164     }
  165     if ($self->{def}{combine} && $v->{isInequality}) {
  166       my $bop = substr($self->{bop},0,1); my $ebop = $bop."=";
  167       if (($v->{bop} eq $bop || $v->{bop} eq $ebop) && $v->{varPos} eq $self->{numPos}) {
  168   $self->{varName} = $v->{varName};
  169   return;
  170       }
  171     }
  172   }
  173   $self->Error("'%s' should have a variable on one side and a number on the other",$self->{bop})
  174     unless $v->{isInequality} && $v->{varPos} eq $self->{numPos};
  175   $self->Error("'%s' can't be combined with '%s'",$v->{bop},$self->{bop});
  176 }
  177 
  178 #
  179 #  Generate the interval for the given type of inequality.
  180 #  If it is a combined inequality, intersect with the other
  181 #  one to get the final set.
  182 #
  183 sub _eval {
  184   my $self = shift; my ($a,$b) = @_;
  185   my $eval = $self->{def}{eval};
  186   my $I = $self->Package("Inequality")->new($self->context,$self->$eval(@_),$self->{varName});
  187   return $I->intersect($a) if Value::isValue($a) && $a->type eq 'Interval';
  188   return $I->intersect($b) if Value::isValue($b) && $b->type eq 'Interval';
  189   return $I;
  190 }
  191 
  192 sub evalLessThan {
  193   my ($self,$a,$b) = @_; my $context = $self->context;
  194   my $I = Value::Infinity->new($context);
  195   return $self->Package("Interval")->new($context,'(',-$I,$b,')') if $self->{varPos} eq 'lop';
  196   return $self->Package("Interval")->new($context,'(',$a,$I,')');
  197 }
  198 
  199 sub evalGreaterThan {
  200   my ($self,$a,$b) = @_; my $context = $self->context;
  201   my $I = Value::Infinity->new;
  202   return $self->Package("Interval")->new($context,'(',$b,$I,')') if $self->{varPos} eq 'lop';
  203   return $self->Package("Interval")->new($context,'(',-$I,$a,')');
  204 }
  205 
  206 sub evalLessThanOrEqualTo {
  207   my ($self,$a,$b) = @_; my $context = $self->context;
  208   my $I = Value::Infinity->new;
  209   return $self->Package("Interval")->new($context,'(',-$I,$b,']') if $self->{varPos} eq 'lop';
  210   return $self->Package("Interval")->new($context,'[',$a,$I,')');
  211 }
  212 
  213 sub evalGreaterThanOrEqualTo {
  214   my ($self,$a,$b) = @_; my $context = $self->context;
  215   my $I = Value::Infinity->new;
  216   return $self->Package("Interval")->new($context,'[',$b,$I,')') if $self->{varPos} eq 'lop';
  217   return $self->Package("Interval")->new($context,'(',-$I,$a,']');
  218 }
  219 
  220 sub evalEqualTo {
  221   my ($self,$a,$b) = @_; my $context = $self->context;
  222   my $x = ($self->{varPos} eq 'lop' ? $b : $a);
  223   return $self->Package("Set")->new($context,$x);
  224 }
  225 
  226 sub evalNotEqualTo {
  227   my ($self,$a,$b) = @_; my $context = $self->context;
  228   my $x = ($self->{varPos} eq 'lop' ? $b : $a);
  229   my $I = Value::Infinity->new;
  230   return $self->Package("Union")->new($context,
  231             $self->Package("Interval")->new($context,'(',-$I,$x,')'),
  232             $self->Package("Interval")->new($context,'(',$x,$I,')')
  233          );
  234 }
  235 
  236 #
  237 #  Inequalities have dummy variables that are not really
  238 #  variables of a formula.
  239 
  240 sub getVariables {{}}
  241 
  242 #
  243 #  Avoid unwanted parentheses from the standard routines.
  244 #
  245 sub string {
  246   my ($self,$precedence) = @_;
  247   my $string; my $bop = $self->{def};
  248 
  249   $string = $self->{lop}->string($bop->{precedence}).
  250             $bop->{string}.
  251             $self->{rop}->string($bop->{precedence});
  252 
  253   return $string;
  254 }
  255 
  256 sub TeX {
  257   my ($self,$precedence) = @_;
  258   my $TeX; my $bop = $self->{def};
  259 
  260   $TeX = $self->{lop}->TeX($bop->{precedence}).
  261          (defined($bop->{TeX}) ? $bop->{TeX} : $bop->{string}) .
  262          $self->{rop}->TeX($bop->{precedence});
  263 
  264   return $TeX;
  265 }
  266 
  267 ##################################################
  268 #
  269 #  Implements the "and" operation as set intersection
  270 #
  271 package Inequalities::BOP::and;
  272 our @ISA = ("Parser::BOP");
  273 
  274 sub _check {
  275   my $self = shift;
  276   $self->Error("The operands of '%s' must be inequalities",$self->{bop})
  277     unless $self->{lop}{isInequality} && $self->{rop}{isInequality};
  278   $self->Error("Inequalities combined by '%s' must both use the same variable",$self->{bop})
  279     unless $self->{lop}{varName} eq $self->{rop}{varName};
  280   $self->{type} = Value::Type("Interval",2);
  281   $self->{varName} = $self->{lop}{varName};
  282   $self->{isInequality} = 1;
  283 }
  284 
  285 sub _eval {$_[1]->intersect($_[2])}
  286 
  287 ##################################################
  288 #
  289 #  Implements the "or" operation as set union
  290 #
  291 package Inequalities::BOP::or;
  292 our @ISA = ("Parser::BOP");
  293 
  294 sub _check {
  295   my $self = shift;
  296   $self->Error("The operands of '%s' must be inequalities",$self->{bop})
  297     unless $self->{lop}{isInequality} && $self->{rop}{isInequality};
  298   $self->Error("Inequalities combined by '%s' must both use the same variable",$self->{bop})
  299     unless $self->{lop}{varName} eq $self->{rop}{varName};
  300   $self->{type} = Value::Type("Interval",2);
  301   $self->{varName} = $self->{lop}{varName};
  302   $self->{isInequality} = 1;
  303 }
  304 
  305 sub _eval {$_[1] + $_[2]}
  306 
  307 ##################################################
  308 #
  309 #  Subclass of Parser::Variable that records whether
  310 #  this variable has already been seen in the formula
  311 #  (so that it can be removed from the formula's
  312 #  variable list when used in an inequality.)
  313 #
  314 package Inequalities::Variable;
  315 our @ISA = ("Parser::Variable");
  316 
  317 sub new {
  318   my $self = shift; my $equation = shift; my $name = shift;
  319   my $isNew = !defined $equation->{variables}{$name};
  320   my $n = $self->SUPER::new($equation,$name,@_);
  321   $n->{isNew} = $isNew;
  322   return $n;
  323 }
  324 
  325 ##################################################
  326 #
  327 #  A special class usd for the variables in
  328 #  inequalities, since they are not really
  329 #  variables for the formula.  (They don't need
  330 #  to be subtituted or given values when the
  331 #  formula is evaluated, and so on.)  These are
  332 #  really just placeholders, here.
  333 #
  334 package Inequalities::DummyVariable;
  335 our @ISA = ("Parser::Item");
  336 
  337 sub new {
  338   my $self = shift; my $class = ref($self) || $self;
  339   my ($equation,$name,$ref) = @_;
  340   my $def = $equation->{context}{variables}{$name};
  341   bless {name => $name, ref => $ref, def => $def, equation => $equation}, $class;
  342 }
  343 
  344 sub eval {shift};
  345 
  346 sub string {(shift)->{name}}
  347 
  348 sub TeX {
  349   my $self = shift; my $name = $self->{name};
  350   return $self->{def}{TeX} if defined $self->{def}{TeX};
  351   $name = $1.'_{'.$2.'}' if ($name =~ m/^([^_]+)_?(\d+)$/);
  352   return $name;
  353 }
  354 
  355 sub perl {
  356   my $self = shift;
  357   return $self->{def}{perl} if defined $self->{def}{perl};
  358   return '$'.$self->{name};
  359 }
  360 
  361 ##################################################
  362 #
  363 #  Give an error when U is used.
  364 #
  365 package Inequalities::BOP::union;
  366 our @ISA = ("Parser::BOP::union");
  367 
  368 sub _check {
  369   my $self = shift;
  370   $self->Error("You can't take unions of inequalities")
  371     if $self->{lop}{isInequality} || $self->{rop}{isInequality};
  372   $self->SUPER::_check(@_);
  373   $self->Error("Unions are not allowed in this context");
  374 }
  375 
  376 ##################################################
  377 #
  378 #  Don't allow sums and differences of inequalities
  379 #
  380 package Inequalities::BOP::add;
  381 our @ISA = ("Parser::BOP::add");
  382 
  383 sub _check {
  384   my $self = shift;
  385   $self->SUPER::_check(@_);
  386   $self->Error("Can't add inequalities (do you mean to use 'or'?)")
  387     if $self->{lop}{isInequality} || $self->{rop}{isInequality};
  388 }
  389 
  390 ##################################################
  391 #
  392 #  Don't allow sums and differences of inequalities
  393 #
  394 package Inequalities::BOP::subtract;
  395 our @ISA = ("Parser::BOP::subtract");
  396 
  397 sub _check {
  398   my $self = shift;
  399   $self->SUPER::_check(@_);
  400   $self->Error("Can't subtract inequalities")
  401     if $self->{lop}{isInequality} || $self->{rop}{isInequality};
  402 }
  403 
  404 ##################################################
  405 #
  406 #  For the Inequalities-Only context, report
  407 #  an error for Intervals, Sets or Union notation.
  408 #
  409 package Inequalities::List::notAllowed;
  410 our @ISA = ("Parser::List::List");
  411 
  412 sub _check {(shift)->Error("You are not allowed to use intervals or sets in this context")}
  413 
  414 
  415 ##################################################
  416 ##################################################
  417 #
  418 #  Subclasses of the Interval, Set, and Union classes
  419 #  that stringify as inequalities
  420 #
  421 
  422 #
  423 #  Some common routines to all three classes
  424 #
  425 package Inequalities::common;
  426 
  427 #
  428 #  Turn the object back into its usual Value version
  429 #
  430 sub demote {
  431   my $self = shift;  my $context = $self->context;
  432   my $other = shift; $other = $self unless defined $other;
  433   return $other unless Value::classMatch($other,"Inequality");
  434   $context->Package($other->type)->make($context,$other->makeData);
  435 }
  436 
  437 #
  438 #  Needed to get Interval data in the right order for make(),
  439 #  and demote all the items in a Union
  440 #
  441 sub makeData {(shift)->value}
  442 
  443 #
  444 #  Recursively mark Intervals and Sets in a Union as Inequalities
  445 #
  446 sub updateParts {}
  447 
  448 #
  449 #  Demote the operands to normal Value objects and
  450 #  perform the action, then remake the result into
  451 #  an Inequality again.
  452 #
  453 sub apply {
  454   my $self = shift; my $context = $self->context;
  455   my $method = shift;  my $other = shift;
  456   $context->Package("Inequality")->new($context,
  457     $self->demote->$method($self->demote($other),@_),
  458     $self->{varName});
  459 }
  460 
  461 sub add {(shift)->apply("add",@_)}
  462 sub sub {(shift)->apply("sub",@_)}
  463 sub reduce {(shift)->apply("reduce",@_)}
  464 sub intersect {(shift)->apply("intersect",@_)}
  465 
  466 #
  467 #  The name to use for error messages in answer checkers
  468 #
  469 sub class {"Inequality"}
  470 sub cmp_class {"an Inequality"}
  471 sub showClass {"an Inequality"}
  472 sub typeRef {
  473   my $self = shift;
  474   return Value::Type($self->type, $self->length, $Value::Type{number});
  475 }
  476 
  477 #
  478 #  Get the precedence based on the type rather than the class.
  479 #
  480 sub precedence {
  481   my $self = shift; my $precedence = $self->context->{precedence};
  482   return $precedence->{$self->type}-$precedence->{Interval}+$precedence->{$self->class};
  483 }
  484 
  485 #
  486 #  Produce better error messages for inequalities
  487 #
  488 sub cmp_checkUnionReduce {
  489   my $self = shift; my $student = shift; my $ans = shift; my $nth = shift || '';
  490   if (Value::classMatch($student,"Inequality")) {
  491     return unless $ans->{studentsMustReduceUnions} &&
  492                   $ans->{showUnionReduceWarnings} &&
  493                   !$ans->{isPreview} && !Value::isFormula($student);
  494     my ($result,$error) = $student->isReduced;
  495     return unless $error;
  496     return {
  497       "overlaps" => "Your$nth answer contains overlapping inequalities",
  498       "overlaps in sets" => "Your$nth answer contains equalities that are already included elsewhere",
  499       "uncombined intervals" => "Your$nth answer can be simplified by combining some inequalities",
  500       #  shouldn't get the following ones from inequalities
  501       "uncombined sets" => "",
  502       "repeated elements in set" => "",
  503       "repeated elements" => "",
  504     }->{$error};
  505   } else {
  506     return unless Value::can($student,"isReduced");
  507     return Value::cmp_checkUnionReduce($self,$student,$ans,$nth,@_)
  508   }
  509 }
  510 
  511 
  512 ##################################################
  513 
  514 package Inequalities::Interval;
  515 our @ISA = ("Inequalities::common", "Value::Interval");
  516 
  517 sub type {"Interval"}
  518 
  519 sub string {
  520   my $self = shift;
  521   my ($a,$b,$open,$close) = $self->value;
  522   my $x = $self->{varName} || ($self->context->variables->names)[0];
  523   $x = $context->{variables}{$x}{string} if defined $context->{variables}{$x}{string};
  524   my $left  = ($open  eq '(' ? ' < ' : ' <= ');
  525   my $right = ($close eq ')' ? ' < ' : ' <= ');
  526   my $inequality = "";
  527   $inequality .= $a->string.$left unless $self->{leftInfinite};
  528   $inequality .= $x;
  529   $inequality .= $right.$b->string unless $self->{rightInfinite};
  530   $inequality = "-infinity < $x < infinity" if $inequality eq $x;
  531   return $inequality;
  532 }
  533 
  534 sub TeX {
  535   my $self = shift;
  536   my ($a,$b,$open,$close) = $self->value;
  537   my $context = $self->context;
  538   my $x = $self->{varName} || ($context->variables->names)[0];
  539   $x = $context->{variables}{$x}{TeX} if defined $context->{variables}{$x}{TeX};
  540   $x =~ s/^([^_]+)_?(\d+)$/$1_{$2}/;
  541   my $left  = ($open  eq '(' ? ' < ' : ' \le ');
  542   my $right = ($close eq ')' ? ' < ' : ' \le ');
  543   my $inequality = "";
  544   $inequality .= $a->string.$left unless $self->{leftInfinite};
  545   $inequality .= $x;
  546   $inequality .= $right.$b->string unless $self->{rightInfinite};
  547   $inequality = "-\\infty < $x < \\infty " if $inequality eq $x;
  548   return $inequality;
  549 }
  550 
  551 ##################################################
  552 
  553 package Inequalities::Union;
  554 our @ISA = ("Inequalities::common", "Value::Union");
  555 
  556 sub type {"Union"}
  557 
  558 #
  559 #  Mark all the parts of the union as inequalities
  560 #
  561 sub updateParts {
  562   my $self = shift;
  563   foreach my $I (@{$self->{data}}) {
  564     $I->{varName} = $self->{varName};
  565     $I->{reduceSets} = $I->{"is".$I->type} = 1;
  566     bless $I, $self->Package("Inequality".$I->type);
  567     $I->updateParts;
  568   }
  569 }
  570 
  571 #
  572 #  Update the intervals and sets when a new union is made
  573 #
  574 sub make {
  575   my $self = (shift)->SUPER::make(@_);
  576   $self->updateParts;
  577   return $self;
  578 }
  579 
  580 #
  581 #  Demote all the items in the union
  582 #
  583 sub makeData {
  584   my $self = shift; my @U = ();
  585   foreach my $I (@{$self->{data}}) {push(@U,$I->demote)}
  586   return @U;
  587 }
  588 
  589 sub string {
  590   my $self = shift;
  591   my $equation = shift; shift; shift; my $prec = shift;
  592   my $op = ($equation->{context} || $self->context)->{operators}{'or'};
  593   my @intervals = ();
  594   foreach my $x (@{$self->data}) {
  595     $x->{format} = $self->{format} if defined $self->{format};
  596     push(@intervals,$x->string($equation))
  597   }
  598   my $string = join($op->{string} || ' or ',@intervals);
  599   $string = '('.$string.')' if defined($prec) && $prec > ($op->{precedence} || 1.5);
  600   return $string;
  601 }
  602 
  603 sub TeX {
  604   my $self = shift;
  605   my $equation = shift; shift; shift; my $prec = shift;
  606   my $op = ($equation->{context} || $self->context)->{operators}{'or'};
  607   my @intervals = ();
  608   foreach my $x (@{$self->data}) {push(@intervals,$x->TeX($equation))}
  609   my $TeX = join($op->{TeX} || $op->{string} || '\hbox{ or }',@intervals);
  610   $TeX = '\left('.$TeX.'\right)' if defined($prec) && $prec > ($op->{precedence} || 1.5);
  611   return $TeX;
  612 }
  613 
  614 ##################################################
  615 
  616 package Inequalities::Set;
  617 our @ISA = ("Inequalities::common", "Value::Set");
  618 
  619 sub type {"Set"}
  620 
  621 sub string {
  622   my $self = shift;  my $equation = shift;
  623   my $x = $self->{varName} || ($self->context->variables->names)[0];
  624   $x = $context->{variables}{$x}{string} if defined $context->{variables}{$x}{string};
  625   my @coords = ();
  626   foreach my $a (@{$self->data}) {
  627     if (Value::isValue($a)) {
  628       $a->{format} = $self->{format} if defined $self->{format};
  629       push(@coords,$x.' = '.$a->string($equation));
  630     } else {
  631       push(@coords,$x.' = '.$a);
  632     }
  633   }
  634   return $self->getFlag('noneWord') unless scalar(@coords);
  635   return join(" or ",@coords);
  636 }
  637 
  638 sub TeX {
  639   my $self = shift;  my $equation = shift;
  640   my $x = $self->{varName} || ($self->context->variables->names)[0];
  641   $x = $context->{variables}{$x}{TeX} if defined $context->{variables}{$x}{TeX};
  642   $x =~ s/^([^_]+)_?(\d+)$/$1_{$2}/;
  643   my @coords = ();
  644   foreach my $a (@{$self->data}) {
  645     if (Value::isValue($a)) {
  646       $a->{format} = $self->{format} if defined $self->{format};
  647       push(@coords,$x.' = '.$a->TeX($equation));
  648     } else {
  649       push(@coords,$x.' = '.$a);
  650     }
  651   }
  652   return '\hbox{'.$self->getFlag('noneWord').'}' unless scalar(@coords);
  653   return join('\hbox{ or }',@coords);
  654 }
  655 
  656 ##################################################
  657 #
  658 #  A class for making inequalities by hand
  659 #
  660 package Inequalities::Inequality;
  661 our @ISA = ('Value');
  662 
  663 sub new {
  664   my $self = shift; my $class = ref($self) || $self;
  665   my $context = (Value::isContext($_[0]) ? shift : $self->context);
  666   my $S = shift; my $x = shift;
  667   $S = Value::makeValue($S,context=>$context);
  668   if (Value::classMatch($S,"Inequality")) {
  669     if (defined($x)) {$S->{varName} = $x; $S->updateParts}
  670     return $S;
  671   }
  672   $x = ($self->context->variables->names)[0] unless $x;
  673   $S = bless $S->inContext($context), $self->Package("Inequality".$S->type);
  674   $S->{varName} = $x; $S->{reduceSets} = $S->{"is".$S->Type} = 1;
  675   $S->updateParts;
  676   return $S;
  677 }
  678 
  679 ##################################################
  680 #
  681 #  Allow Interval() to coerce types to Value::Interval
  682 #
  683 package Inequalities::MakeInterval;
  684 our @ISA = ("Value::Interval");
  685 
  686 sub new {
  687   my $self = shift;
  688   $self = $self->SUPER::new(@_);
  689   $self = $self->demote if $self->classMatch("Inequality");
  690   return $self;
  691 }
  692 
  693 ##################################################
  694 #
  695 #  Mark this as a list of inequalities (if it is)
  696 #
  697 package Inequalities::List;
  698 our @ISA = ("Value::List");
  699 
  700 sub new {
  701   my $self = (shift)->SUPER::new(@_);
  702   return $self unless $self->{type} =~ m/^(unknown|Interval|Set|Union)$/;
  703   foreach my $x (@{$self->{data}}) {return $self unless Value::classMatch($x,'Inequality')}
  704   $self->{type} = 'Inequality';
  705   return $self;
  706 }
  707 
  708 package Inequalities::List::List;
  709 our @ISA = ("Parser::List::List");
  710 
  711 sub _check {
  712   my $self = shift; $self->SUPER::_check(@_);
  713   if ($self->canBeInUnion) {
  714     #
  715     #  Convert lists that look like intervals into intervals
  716     #  and then check if they are OK.
  717     #
  718     bless $self, $self->context->{lists}{Interval}{class};
  719     $self->{type} = $Value::Type{interval};
  720     $self->{parens} = $self->context->{parens}{interval};
  721     $self->_check;
  722   } else {
  723     my $entryType = $self->typeRef->{entryType};
  724     return unless $entryType->{name} =~ m/^(unknown|Interval|Set|Union)$/;
  725     foreach my $x (@{$self->{coords}}) {return unless $x->{isInequality}};
  726     $entryType->{name} = "Inequality";
  727   }
  728 }
  729 
  730 ##################################################
  731 
  732 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9