[system] / trunk / pg / lib / Value.pm Repository:
ViewVC logotype

View of /trunk/pg/lib/Value.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3716 - (download) (as text) (annotate)
Sun Oct 16 03:37:17 2005 UTC (14 years, 1 month ago) by dpvc
File size: 18983 byte(s)
In the past, when Value objects were inserted into strings, they would
automatically include parentheses so that if you had $f equal to 1+x
and $g equal to 1-x, then Formula("$f/$g") would mean (1+x)/(1-x)
rather than 1+(x/1)-x, which is what would happen as a straing string
substitution.

The problem is that this would also happen for real numbers, vectors,
and everything else, even when it wasn't necessary.  So if $x=Real(3),
then "Let x = $x" would be "Let x = (3)".

I have changed the behavior of the string concatenation for Value
objects so that parentheses are only added in a few cases: for
Formulas, Complex numbers, and Unions.  This makes the other Value
objects work more like regular variables in strings, but might cause
some problems with strings that are used as formulas.  For example, if
$a = Real(-3), then "x + 2 $a" will become "x + 2 -3", or "x-1" rather
than the expected "x - 6".  (The old approach would have made it "x +
2 (-3)" which would have worked properly).  For the most part, it is
easier to use something like "x + 2*$a" or even "x" + 2*$a in this
case, so the extra trouble of having to avoid parentheses when you
really meant to substitute the value into a string didn't seem worth
it.

    1 package Value;
    2 my $pkg = 'Value';
    3 use vars qw($context $defaultContext %Type);
    4 use strict;
    5 
    6 #############################################################
    7 #
    8 #  Initialize the context
    9 #
   10 
   11 use Value::Context;
   12 
   13 $defaultContext = Value::Context->new(
   14   lists => {
   15     'Point'  => {open => '(', close => ')'},
   16     'Vector' => {open => '<', close => '>'},
   17     'Matrix' => {open => '[', close => ']'},
   18     'List'   => {open => '(', close => ')'},
   19     'Set'    => {open => '{', close => '}'},
   20   },
   21   flags => {
   22     #
   23     #  For vectors:
   24     #
   25     ijk => 0,  # print vectors as <...>
   26     #
   27     #  For strings:
   28     #
   29     allowEmptyStrings => 1,
   30     infiniteWord => 'infinity',
   31     #
   32     #  For intervals and unions:
   33     #
   34     ignoreEndpointTypes => 0,
   35     reduceSets => 1,
   36     reduceSetsForComparison => 1,
   37     reduceUnions => 1,
   38     reduceUnionsForComparison => 1,
   39     #
   40     #  For fuzzy reals:
   41     #
   42     useFuzzyReals => 1,
   43     tolerance    => 1E-4,
   44     tolType      => 'relative',
   45     zeroLevel    => 1E-14,
   46     zeroLevelTol => 1E-12,
   47     #
   48     #  For Formulas:
   49     #
   50     limits       => [-2,2],
   51     num_points   => 5,
   52     granularity  => 1000,
   53     resolution   => undef,
   54     max_adapt    => 1E8,
   55     checkUndefinedPoints => 0,
   56     max_undefined => undef,
   57   },
   58 );
   59 
   60 $context = \$defaultContext;
   61 
   62 
   63 #
   64 #  Precedence of the various types
   65 #    (They will be promoted upward automatically when needed)
   66 #
   67 $$context->{precedence} = {
   68    'Number'   =>  0,
   69    'Real'     =>  1,
   70    'Infinity' =>  2,
   71    'Complex'  =>  3,
   72    'Point'    =>  4,
   73    'Vector'   =>  5,
   74    'Matrix'   =>  6,
   75    'List'     =>  7,
   76    'Interval' =>  8,
   77    'Set'      =>  9,
   78    'Union'    => 10,
   79    'String'   => 11,
   80    'Formula'  => 12,
   81    'special'  => 20,
   82 };
   83 
   84 #
   85 #  Binding of perl operator to class method
   86 #
   87 $$context->{method} = {
   88    '+'   => 'add',
   89    '-'   => 'sub',
   90    '*'   => 'mult',
   91    '/'   => 'div',
   92    '**'  => 'power',
   93    '.'   => '_dot',       # see _dot below
   94    'x'   => 'cross',
   95    '<=>' => 'compare',
   96    'cmp' => 'compare_string',
   97 };
   98 
   99 $$context->{pattern}{infinite} = '[-+]?inf(?:inity)?';
  100 $$context->{pattern}{infinity} = '\+?inf(?:inity)?';
  101 $$context->{pattern}{-infinity} = '-inf(?:inity)?';
  102 
  103 push(@{$$context->{data}{values}},'method','precedence');
  104 
  105 #
  106 #  Get the value of a flag from the object itself,
  107 #  or from the context, or from the default context
  108 #  or from the given default, whichever is found first.
  109 #
  110 sub getFlag {
  111   my $self = shift; my $name = shift;
  112   return $self->{$name} if ref($self) && defined($self->{$name});
  113   return $self->{context}{flags}{$name} if ref($self) && defined($self->{context}{flags}{$name});
  114   return $$Value::context->{flags}{$name} if defined($$Value::context->{flags}{$name});
  115   return shift;
  116 }
  117 
  118 sub copy {
  119   my $self = shift;
  120   my $copy = {%{$self}}; $copy->{data} = [@{$self->{data}}];
  121   foreach my $x (@{$copy->{data}}) {$x = $x->copy if Value::isValue($x)}
  122   return bless $copy, ref($self);
  123 }
  124 
  125 #############################################################
  126 
  127 #
  128 #  Check if a value is a number, complex, etc.
  129 #
  130 sub matchNumber   {my $n = shift; $n =~ m/^$$context->{pattern}{signedNumber}$/i}
  131 sub matchInfinite {my $n = shift; $n =~ m/^$$context->{pattern}{infinite}$/i}
  132 sub isReal    {class(shift) eq 'Real'}
  133 sub isComplex {class(shift) eq 'Complex'}
  134 sub isFormula {
  135   my $v = shift;
  136   return class($v) eq 'Formula' ||
  137          (ref($v) && ref($v) ne 'ARRAY' && $v->{isFormula});
  138 }
  139 sub isValue   {
  140   my $v = shift;
  141   return (ref($v) || $v) =~ m/^Value::/ ||
  142          (ref($v) && ref($v) ne 'ARRAY' && $v->{isValue});
  143 }
  144 
  145 sub isNumber {
  146   my $n = shift;
  147   return $n->{tree}->isNumber if isFormula($n);
  148   return isReal($n) || isComplex($n) || matchNumber($n);
  149 }
  150 
  151 sub isRealNumber {
  152   my $n = shift;
  153   return $n->{tree}->isRealNumber if isFormula($n);
  154   return isReal($n) || matchNumber($n);
  155 }
  156 
  157 sub isZero {
  158   my $self = shift;
  159   return 0 if scalar(@{$self->{data}}) == 0;
  160   foreach my $x (@{$self->{data}}) {return 0 unless $x eq "0"}
  161   return 1;
  162 }
  163 
  164 sub isOne {0}
  165 
  166 sub isSetOfReals {0}
  167 sub canBeInUnion {
  168   my $self = shift;
  169   return $self->length == 2 && $self->typeRef->{entryType}{name} eq 'Number' &&
  170     $self->{open} =~ m/^[\(\[]$/ && $self->{close} =~ m/^[\)\]]$/;
  171 }
  172 
  173 #
  174 #  Convert non-Value objects to Values, if possible
  175 #
  176 sub makeValue {
  177   my $x = shift; my %params = (showError => 0, makeFormula => 1, @_);
  178   return $x if ref($x) && ref($x) ne 'ARRAY';
  179   return Value::Real->make($x) if matchNumber($x);
  180   if (matchInfinite($x)) {
  181     my $I = Value::Infinity->new();
  182     $I = $I->neg if $x =~ m/^$$Value::context->{pattern}{-infinity}$/;
  183     return $I;
  184   }
  185   return Value::String->make($x)
  186     if !$Parser::installed || $$Value::context->{strings}{$x} ||
  187        ($x eq '' && $$Value::context->{flags}{allowEmptyStrings});
  188   return $x if !$params{makeFormula};
  189   Value::Error("String constant '%s' is not defined in this context",$x)
  190     if $params{showError};
  191   $x = Value::Formula->new($x);
  192   $x = $x->eval if $x->isConstant;
  193   return $x;
  194 }
  195 
  196 #
  197 #  Get a printable version of the class of an object
  198 #
  199 sub showClass {
  200   my $value = makeValue(shift,makeFormula=>0);
  201   return "'".$value."'" unless Value::isValue($value);
  202   my $class = class($value);
  203   return showType($value) if ($class eq 'List');
  204   $class .= ' Number' if $class =~ m/^(Real|Complex)$/;
  205   $class .= ' of Intervals' if $class eq 'Union';
  206   $class = 'Word' if $class eq 'String';
  207   return 'a Formula that returns '.showType($value->{tree}) if ($class eq 'Formula');
  208   return 'an '.$class if $class =~ m/^[aeio]/i;
  209   return 'a '.$class;
  210 }
  211 
  212 #
  213 #  Get a printable version of the type of an object
  214 #
  215 sub showType {
  216   my $value = shift;
  217   my $type = $value->type;
  218   if ($type eq 'List') {
  219     my $ltype = $value->typeRef->{entryType}{name};
  220     if ($ltype && $ltype ne 'unknown') {
  221       $ltype =~ s/y$/ie/;
  222       $type .= ' of '.$ltype.'s';
  223     }
  224   }
  225   return 'an Infinity' if $type eq 'String' && $value->{isInfinite};
  226   return 'a Word' if $type eq 'String';
  227   return 'a Complex Number' if $value->isComplex;
  228   return 'an '.$type if $type =~ m/^[aeio]/i;
  229   return 'a '.$type;
  230 }
  231 
  232 #
  233 #  Return a string describing a value's type
  234 #
  235 sub getType {
  236   my $equation = shift; my $value = shift;
  237   my $strings = $equation->{context}{strings};
  238   if (ref($value) eq 'ARRAY') {
  239     return 'Interval' if ($value->[0] =~ m/^[(\[]$/ && $value->[-1] =~ m/^[)\]]$/);
  240     my ($type,$ltype);
  241     foreach my $x (@{$value}) {
  242       $type = getType($equation,$x);
  243       if ($type eq 'value') {
  244         $type = $x->type if $x->class eq 'Formula';
  245         $type = 'Number' if $x->class eq 'Complex' || $type eq 'Complex';
  246       }
  247       $ltype = $type if $ltype eq '';
  248       return 'List' if $type ne $ltype;
  249     }
  250     return 'Point' if $ltype eq 'Number';
  251     return 'Matrix' if $ltype =~ m/Point|Matrix/;
  252     return 'List';
  253   }
  254   elsif (Value::isFormula($value)) {return 'Formula'}
  255   elsif (Value::class($value) eq 'Infinity') {return 'Infinity'}
  256   elsif (Value::isReal($value)) {return 'Number'}
  257   elsif (Value::isValue($value)) {return 'value'}
  258   elsif (ref($value)) {return 'unknown'}
  259   elsif (defined($strings->{$value})) {return 'String'}
  260   elsif (Value::isNumber($value)) {return 'Number'}
  261   elsif ($value eq '' && $equation->{context}{flags}{allowEmptyStrings}) {return 'String'}
  262   return 'unknown';
  263 }
  264 
  265 #
  266 #  Get a string describing a value's type,
  267 #    and convert the value to a Value object (if needed)
  268 #
  269 sub getValueType {
  270   my $equation = shift; my $value = shift;
  271   my $type = Value::getType($equation,$value);
  272   if ($type eq 'String') {$type = $Value::Type{string}}
  273   elsif ($type eq 'Number') {$type = $Value::Type{number}}
  274   elsif ($type eq 'Infinity') {$type = $Value::Type{infinity}}
  275   elsif ($type eq 'value' || $type eq 'Formula') {$type = $value->typeRef}
  276   elsif ($type eq 'unknown') {
  277     $equation->Error(["Can't convert %s to a constant",Value::showClass($value)]);
  278   } else {
  279     $type = 'Value::'.$type, $value = $type->new(@{$value});
  280     $type = $value->typeRef;
  281   }
  282   return ($value,$type);
  283 }
  284 
  285 #
  286 #  Convert a list of values to a list of formulas (called by Parser::Value)
  287 #
  288 sub toFormula {
  289   my $formula = shift;
  290   my $processed = 0;
  291   my @f = (); my $vars = {};
  292   foreach my $x (@_) {
  293     if (isFormula($x)) {
  294       $formula->{context} = $x->{context}, $processed = 1 unless $processed;
  295       $formula->{variables} = {%{$formula->{variables}},%{$x->{variables}}};
  296       push(@f,$x->{tree}->copy($formula));
  297     } else {
  298       push(@f,$formula->{context}{parser}{Value}->new($formula,$x));
  299     }
  300   }
  301   return (@f);
  302 }
  303 
  304 #
  305 #  Convert a list of values (and open and close parens)
  306 #    to a formula whose type is the list type associated with
  307 #    the parens.
  308 #
  309 sub formula {
  310   my $self = shift; my $values = shift;
  311   my $class = $self->class;
  312   my $list = $$context->lists->get($class);
  313   my $open = $list->{'open'};
  314   my $close = $list->{'close'};
  315   my $paren = $open; $paren = 'list' if $class eq 'List';
  316   my $formula = Value::Formula->blank;
  317   my @coords = Value::toFormula($formula,@{$values});
  318   $formula->{tree} = $formula->{context}{parser}{List}->new($formula,[@coords],0,
  319      $formula->{context}{parens}{$paren},$coords[0]->typeRef,$open,$close);
  320   $formula->{autoFormula} = 1;  # mark that this was generated automatically
  321   return $formula;
  322 }
  323 
  324 #
  325 #  A shortcut for new() that creates an instance of the object,
  326 #    but doesn't do the error checking.  We assume the data are already
  327 #    known to be good.
  328 #
  329 sub make {
  330   my $self = shift; my $class = ref($self) || $self;
  331   bless {data => [@_]}, $class;
  332 }
  333 
  334 #
  335 #  Easy method for setting parameters of an object
  336 #
  337 sub with {
  338   my $self = shift; my %hash = @_;
  339   foreach my $id (keys(%hash)) {$self->{$id} = $hash{$id}}
  340   return $self;
  341 }
  342 
  343 #
  344 #  Return a type structure for the item
  345 #    (includes name, length of vectors, and so on)
  346 #
  347 sub Type {
  348   my $name = shift; my $length = shift; my $entryType = shift;
  349   $length = 1 unless defined $length;
  350   return {name => $name, length => $length, entryType => $entryType,
  351           list => (defined $entryType), @_};
  352 }
  353 
  354 #
  355 #  Some predefined types
  356 #
  357 %Type = (
  358   number   => Value::Type('Number',1),
  359   complex  => Value::Type('Number',2),
  360   string   => Value::Type('String',1),
  361   infinity => Value::Type('Infinity',1),
  362   unknown  => Value::Type('unknown',0,undef,list => 1)
  363 );
  364 
  365 #
  366 #  Return various information about the object
  367 #
  368 sub value {return @{(shift)->{data}}}                  # the value of the object (as an array)
  369 sub data {return (shift)->{data}}                      # the reference to the value
  370 sub length {return scalar(@{(shift)->{data}})}         # the number of coordinates
  371 sub type {return (shift)->typeRef->{name}}             # the object type
  372 sub entryType {return (shift)->typeRef->{entryType}}   # the coordinate type
  373 #
  374 #  The the full type-hash for the item
  375 #
  376 sub typeRef {
  377   my $self = shift;
  378   return Value::Type($self->class, $self->length, $Value::Type{number});
  379 }
  380 #
  381 #  The Value.pm object class
  382 #
  383 sub class {
  384   my $self = shift; my $class = ref($self) || $self;
  385   $class =~ s/.*:://;
  386   return $class;
  387 }
  388 
  389 #
  390 #  Get an element from a point, vector, matrix, or list
  391 #
  392 sub extract {
  393   my $M = shift; my $i; my @indices = @_;
  394   return unless Value::isValue($M);
  395   @indices = $_[0]->value if scalar(@_) == 1 && Value::isValue($_[0]);
  396   while (scalar(@indices) > 0) {
  397     $i = shift @indices; $i-- if $i > 0; $i = $i->value if Value::isValue($i);
  398     Value::Error("Can't extract element number '%s' (index must be an integer)",$i)
  399       unless $i =~ m/^-?\d+$/;
  400     $M = $M->data->[$i];
  401   }
  402   return $M;
  403 }
  404 
  405 
  406 #
  407 #  Promote an operand to the same precedence as the current object
  408 #
  409 sub promotePrecedence {
  410   my $self = shift; my $other = shift;
  411   return 0 unless Value::isValue($other);
  412   my $sprec = $$context->{precedence}{class($self)};
  413   my $oprec = $$context->{precedence}{class($other)};
  414   return (defined($sprec) && defined($oprec) && $sprec < $oprec);
  415 }
  416 
  417 sub promote {shift}
  418 
  419 #
  420 #  Default stub to call when no function is defined for an operation
  421 #
  422 sub nomethod {
  423   my ($l,$r,$flag,$op) = @_;
  424   my $call = $$context->{method}{$op};
  425   if (defined($call) && $l->promotePrecedence($r)) {return $r->$call($l,!$flag)}
  426   my $error = "Can't use '%s' with %s-valued operands";
  427   $error .= " (use '**' for exponentiation)" if $op eq '^';
  428   Value::Error($error,$op,$l->class);
  429 }
  430 
  431 #
  432 #  Stubs for the sub-classes
  433 #
  434 sub add   {nomethod(@_,'+')}
  435 sub sub   {nomethod(@_,'-')}
  436 sub mult  {nomethod(@_,'*')}
  437 sub div   {nomethod(@_,'/')}
  438 sub power {nomethod(@_,'**')}
  439 sub cross {nomethod(@_,'x')}
  440 
  441 #
  442 #  If the right operand is higher precedence, we switch the order.
  443 #
  444 #  If the right operand is also a Value object, we do the object's
  445 #  dot method to combine the two objects of the same class.
  446 #
  447 #  Otherwise, since . is used for string concatenation, we want to retain
  448 #  that.  Since the resulting string is often used in Formula and will be
  449 #  parsed again, we put parentheses around the values to guarantee that
  450 #  the values will be treated as one mathematical unit.  For example, if
  451 #  $f = Formula("1+x") and $g = Formula("y") then Formula("$f/$g") will be
  452 #  (1+x)/y not 1+(x/y), as it would be without the implicit parentheses.
  453 #
  454 sub _dot {
  455   my ($l,$r,$flag) = @_;
  456   return $r->_dot($l,!$flag) if ($l->promotePrecedence($r));
  457   return $l->dot($r,$flag) if (Value::isValue($r));
  458   if ($$Value::context->flag('StringifyAsTeX')) {$l = $l->TeX} else {$l = $l->pdot}
  459   return ($flag)? ($r.$l): ($l.$r);
  460 }
  461 #
  462 #  Some classes override this
  463 #
  464 sub dot {
  465   my ($l,$r,$flag) = @_;
  466   my $tex = $$Value::context->flag('StringifyAsTeX');
  467   if ($tex) {$l = $l->TeX} else {$l = $l->pdot}
  468   if (ref($r)) {if ($tex) {$r = $r->TeX} else {$r = $r->pdot}}
  469   return ($flag)? ($r.$l): ($l.$r);
  470 }
  471 
  472 #
  473 #  Some classes override this to add parens
  474 #
  475 sub pdot {shift->stringify}
  476 
  477 
  478 #
  479 #  Compare the values of the objects
  480 #    (list classes should replace this)
  481 #
  482 sub compare {
  483   my ($l,$r,$flag) = @_;
  484   if ($l->promotePrecedence($r)) {return $r->compare($l,!$flag)}
  485   return $l->value <=> $r->value;
  486 }
  487 
  488 #
  489 #  Compare the values as strings
  490 #
  491 sub compare_string {
  492   my ($l,$r,$flag) = @_;
  493   if ($l->promotePrecedence($r)) {return $r->compare_string($l,!$flag)}
  494   $l = $l->stringify; $r = $r->stringify if Value::isValue($r);
  495   if ($flag) {my $tmp = $l; $l = $r; $r = $tmp}
  496   return $l cmp $r;
  497 }
  498 
  499 #
  500 #  Generate the various output formats
  501 #  (can be replaced by sub-classes)
  502 #
  503 sub stringify {
  504   my $self = shift;
  505   return $self->TeX() if $$Value::context->flag('StringifyAsTeX');
  506   my $def = $$Value::context->lists->get($self->class);
  507   return $self->string unless $def;
  508   my $open = $self->{open};   $open  = $def->{open}  unless defined($open);
  509   my $close = $self->{close}; $close = $def->{close} unless defined($close);
  510   $open.join($def->{separator},@{$self->data}).$close;
  511 }
  512 
  513 sub string {
  514   my $self = shift; my $equation = shift;
  515   my $def = ($equation->{context} || $$Value::context)->lists->get($self->class);
  516   return $self->value unless $def;
  517   my $open = shift; my $close = shift;
  518   $open  = $self->{open}  unless defined($open);
  519   $open  = $def->{open}   unless defined($open);
  520   $close = $self->{close} unless defined($close);
  521   $close = $def->{close}  unless defined($close);
  522   my @coords = ();
  523   foreach my $x (@{$self->data}) {
  524     if (Value::isValue($x))
  525       {push(@coords,$x->string($equation))} else {push(@coords,$x)}
  526   }
  527   return $open.join($def->{separator},@coords).$close;
  528 }
  529 
  530 sub TeX {
  531   my $self = shift; my $equation = shift;
  532   my $context = $equation->{context} || $$Value::context;
  533   my $def = $context->lists->get($self->class);
  534   return $self->string(@_) unless $def;
  535   my $open = shift; my $close = shift;
  536   $open  = $self->{open}  unless defined($open);
  537   $open  = $def->{open}   unless defined($open);
  538   $close = $self->{close} unless defined($close);
  539   $close = $def->{close}  unless defined($close);
  540   $open =~ s/([{}])/\\$1/g; $close =~ s/([{}])/\\$1/g;
  541   $open = '\left'.$open if $open; $close = '\right'.$close if $close;
  542   my @coords = (); my $str = $context->{strings};
  543   foreach my $x (@{$self->data}) {
  544     if (Value::isValue($x)) {push(@coords,$x->TeX($equation))}
  545     elsif (defined($str->{$x}) && $str->{$x}{TeX}) {push(@coords,$str->{$x}{TeX})}
  546     else {push(@coords,$x)}
  547   }
  548   return $open.join(',',@coords).$close;
  549 }
  550 
  551 #
  552 #  For perl, call the appropriate constructor around the object's data
  553 #
  554 sub perl {
  555   my $self = shift; my $parens = shift; my $matrix = shift;
  556   my $class = $self->class;
  557   my $mtype = $class eq 'Matrix'; $mtype = -1 if $mtype & !$matrix;
  558   my $perl; my @p = ();
  559   foreach my $x (@{$self->data}) {
  560     if (Value::isValue($x)) {push(@p,$x->perl(0,$mtype))} else {push(@p,$x)}
  561   }
  562   @p = ("'".$self->{open}."'",@p,"'".$self->{close}."'") if $class eq 'Interval';
  563   if ($matrix) {
  564     $perl = join(',',@p);
  565     $perl = '['.$perl.']' if $mtype > 0;
  566   } else {
  567     $perl = 'new '.ref($self).'('.join(',',@p).')';
  568     $perl = "($perl)->with(open=>'$self->{open}',close=>'$self->{close}')"
  569       if $class eq 'List' && $self->{open}.$self->{close} ne '()';
  570     $perl = '('.$perl.')' if $parens == 1;
  571   }
  572   return $perl;
  573 }
  574 
  575 #
  576 #  Stubs for when called by Parser
  577 #
  578 sub eval {shift}
  579 sub reduce {shift}
  580 
  581 sub ijk {
  582   Value::Error("Can't use method 'ijk' with objects of type '%s'",(shift)->class);
  583 }
  584 
  585 #
  586 #  Report an error
  587 #
  588 sub Error {
  589   my $message = shift;
  590   $message = [$message,@_] if scalar(@_) > 0;
  591   $$context->setError($message,'');
  592   $message = $$context->{error}{message};
  593   die $message . traceback() if $$context->flags('showTraceback');
  594   die $message . getCaller();
  595 }
  596 
  597 #
  598 #  Try to locate the line and file where the error occurred
  599 #
  600 sub getCaller {
  601   my $frame = 2;
  602   while (my ($pkg,$file,$line,$subname) = caller($frame++)) {
  603     return " at line $line of $file\n"
  604       unless $pkg =~ /^(Value|Parser)/ ||
  605              $subname =~ m/^(Value|Parser).*(new|call)$/;
  606   }
  607   return "";
  608 }
  609 
  610 #
  611 #  For debugging
  612 #
  613 sub traceback {
  614   my $frame = shift; $frame = 2 unless defined($frame);
  615   my $trace = '';
  616   while (my ($pkg,$file,$line,$subname) = caller($frame++))
  617     {$trace .= " in $subname at line $line of $file\n"}
  618   return $trace;
  619 }
  620 
  621 ###########################################################################
  622 #
  623 #  Load the sub-classes.
  624 #
  625 
  626 use Value::Real;
  627 use Value::Complex;
  628 use Value::Infinity;
  629 use Value::Point;
  630 use Value::Vector;
  631 use Value::Matrix;
  632 use Value::List;
  633 use Value::Interval;
  634 use Value::Set;
  635 use Value::Union;
  636 use Value::String;
  637 use Value::Formula;
  638 
  639 use Value::WeBWorK;  # stuff specific to WeBWorK
  640 
  641 ###########################################################################
  642 
  643 use vars qw($installed);
  644 $Value::installed = 1;
  645 
  646 ###########################################################################
  647 ###########################################################################
  648 #
  649 #    To Do:
  650 #
  651 #  Make Complex class include more of Complex1.pm
  652 #  Make better interval comparison
  653 #  Include context in objects within new() calls.
  654 #
  655 ###########################################################################
  656 
  657 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9