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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 4991 - (download) (as text) (annotate)
Fri Jun 8 02:09:21 2007 UTC (6 years ago) by dpvc
File size: 7629 byte(s)
Update new() and make() methods to accept a context as the first
parameter (making it easier to create objects in a given context
without having to resort to a separate call to coerce them to the
given context after the fact).

    1 ###########################################################################
    2 #
    3 #  Implements the Interval class
    4 #
    5 package Value::Interval;
    6 my $pkg = 'Value::Interval';
    7 
    8 use strict;
    9 our @ISA = qw(Value);
   10 
   11 #
   12 #  Convert a value to an interval.  The value consists of
   13 #    an open paren string, one or two real numbers or infinities,
   14 #    and a close paren string.
   15 #
   16 sub new {
   17   my $self = shift; my $class = ref($self) || $self;
   18   my $context = (Value::isContext($_[0]) ? shift : $self->context);
   19   if (scalar(@_) == 1 && (!ref($_[0]) || ref($_[0]) eq 'ARRAY')) {
   20     my $x = Value::makeValue($_[0],context=>$context);
   21     if (Value::isFormula($x)) {
   22       return $x if $x->type eq 'Interval';
   23       Value::Error("Formula does not return an Interval");
   24     }
   25     return $self->promote($x);
   26   }
   27   my @params = @_;
   28   Value::Error("Interval can't be empty") unless scalar(@params) > 0;
   29   Value::Error("Extra arguments for Interval()") if scalar(@params) > 4;
   30   return $self->Package("Set")->new($context,@params) if scalar(@params) == 1;
   31   @params = ('(',@params,')') if (scalar(@params) == 2);
   32   my ($open,$a,$b,$close) = @params;
   33   if (!defined($close)) {$close = $b; $b = $a}
   34   $a = Value::makeValue($a,context=>$context); $b = Value::makeValue($b,context=>$context);
   35   return $self->formula($open,$a,$b,$close) if Value::isFormula($a) || Value::isFormula($b);
   36   Value::Error("Endpoints of intervals must be numbers or infinities") unless
   37     isNumOrInfinity($a) && isNumOrInfinity($b);
   38   my ($ia,$ib) = (isInfinity($a),isInfinity($b));
   39   my ($nia,$nib) = (isNegativeInfinity($a),isNegativeInfinity($b));
   40   Value::Error("Can't make an interval only out of Infinity") if ($ia && $ib) || ($nia && $nib);
   41   Value::Error("Left endpoint must be less than right endpoint")
   42     unless $nia || $ib || ($a <= $b && !$ia && !$nib);
   43   $open  = '(' if $open  eq '[' && $nia; # should be error ?
   44   $close = ')' if $close eq ']' && $ib;  # ditto?
   45   Value::Error("Open parenthesis of interval must be '(' or '['")
   46     unless $open eq '(' || $open eq '[';
   47   Value::Error("Close parenthesis of interval must be ')' or ']'")
   48     unless $close eq ')' || $close eq ']';
   49   return $self->formula($open,$a,$b,$close)
   50     if Value::isFormula($a) || Value::isFormula($b);
   51   Value::Error("Single point intervals must use '[' and ']'")
   52     if $a == $b && ($open ne '[' || $close ne ']');
   53   bless {
   54     data => [$a,$b], open => $open, close => $close,
   55     leftInfinite => $nia, rightInfinite => $ib,
   56     context => $context,
   57   }, $class;
   58 }
   59 
   60 #
   61 #  Similarly for make, but without the error checks
   62 #
   63 sub make {
   64   my $self = shift; my $class = ref($self) || $self;
   65   my ($open,$a,$b,$close) = @_;
   66   $close = $b, $b = $a unless defined($close);
   67   bless {
   68     data => [$a,$b], open => $open, close => $close,
   69     leftInfinite => isNegativeInfinity($a), rightInfinite => isInfinity($b),
   70     context => $self->context,
   71   }, $class
   72 }
   73 
   74 #
   75 #  Make a formula out of the data for an interval
   76 #
   77 sub formula {
   78   my $self = shift;
   79   my ($open,$a,$b,$close) = @_;
   80   my $formula = $self->Package("Formula")->blank($self->context);
   81   ($a,$b) = Value::toFormula($formula,$a,$b);
   82   $formula->{tree} = $formula->{context}{parser}{List}->new($formula,[$a,$b],0,
   83      $formula->{context}{parens}{$open},$Value::Type{number},$open,$close);
   84   return $formula;
   85 }
   86 
   87 #
   88 #  Tests for infinities
   89 #
   90 sub isNumOrInfinity {
   91   my $n = shift;
   92   return isInfinity($n) || isNegativeInfinity($n) || Value::isNumber($n);
   93 }
   94 sub isInfinity {
   95   my $n = shift;
   96   return $n->{tree}{isInfinity} if Value::isFormula($n);
   97   $n = Value::makeValue($n); return 0 unless ref($n);
   98   return $n->{isInfinite} && !$n->{isNegative};
   99 }
  100 sub isNegativeInfinity {
  101   my $n = shift;
  102   return $n->{tree}{isNegativeInfinity} if Value::isFormula($n);
  103   $n = Value::makeValue($n); return 0 unless ref($n);
  104   return $n->{isInfinite} && $n->{isNegative};
  105 }
  106 
  107 sub isOne {0}
  108 sub isZero {0}
  109 
  110 sub canBeInUnion {1}
  111 sub isSetOfReals {1}
  112 
  113 #
  114 #  Return the open and close parens as well as the endpoints
  115 #
  116 sub value {
  117   my $self = shift;
  118   my ($a,$b) = @{$self->data};
  119   return ($a,$b,$self->{open},$self->{close});
  120 }
  121 
  122 #
  123 #  Return the number of endpoints
  124 #
  125 sub length {
  126   my $self = shift;
  127   my ($a,$b) = $self->data;
  128   return $a == $b ? 1 : 2;
  129 }
  130 
  131 #
  132 #  Convert points and lists to intervals, when needed
  133 #
  134 sub promote {
  135   my $self = shift; my $x = (scalar(@_) ? shift : $self);
  136   $x = Value::makeValue($x,context=>$self->context);
  137   return $self->new($x,@_) if scalar(@_) > 0;
  138   return $x if $x->isSetOfReals;
  139   return $self->Package("Set")->new($self->context,$x) if Value::isReal($x);
  140   my $open  = $x->{open};  $open  = '(' unless defined($open);
  141   my $close = $x->{close}; $close = ')' unless defined($close);
  142   return $self->new($open,$x->value,$close) if $x->canBeInUnion;
  143   Value::Error("Can't convert %s to %s",$x->showClass,$self->showClass);
  144 }
  145 
  146 ############################################
  147 #
  148 #  Operations on intervals
  149 #
  150 
  151 #
  152 #  Addition forms unions
  153 #
  154 sub add {
  155   my ($self,$l,$r) = Value::checkOpOrder(@_);
  156   Value::Union::form($self->context,$l,$r);
  157 }
  158 sub dot {my $self = shift; $self->add(@_)}
  159 
  160 #
  161 #  Subtraction can split into a union
  162 #
  163 sub sub {
  164   my ($self,$l,$r) = Value::checkOpOrder(@_);
  165   Value::Union::form($self->context,subIntervalInterval($l,$r));
  166 }
  167 
  168 #
  169 #  Subtract an interval from another
  170 #    (returns the resulting interval(s), set
  171 #     or nothing for emtpy set)
  172 #
  173 sub subIntervalInterval {
  174   my ($l,$r) = @_; $l = $l->copy; $r = $r->copy;
  175   my ($a,$b) = $l->value; my ($c,$d) = $r->value;
  176   my $self = $l; my $context = $self->context;
  177   my @union = ();
  178   if ($d <= $a) {
  179     $l->{open} = '(' if $d == $a && $r->{close} eq ']';
  180     push(@union,$l) unless $a == $b && $l->{open} eq '(';
  181   } elsif ($c >= $b) {
  182     $l->{close} = ')' if $c == $b && $r->{open} eq '[';
  183     push(@union,$l) unless $a == $b && $l->{close} eq ')';
  184   } else {
  185     if ($a == $c) {
  186       push(@union,$self->Package("Set")->make($context,$a))
  187   if $l->{open} eq '[' && $r->{open} eq '(';
  188     } elsif ($a < $c) {
  189       my $close = ($r->{open} eq '[')? ')': ']';
  190       push(@union,$self->Package("Interval")->make($context,$l->{open},$a,$c,$close));
  191     }
  192     if ($d == $b) {
  193       push(@union,$self->Package("Set")->make($context,$b))
  194   if $l->{close} eq ']' && $r->{close} eq ')';
  195     } elsif ($d < $b) {
  196       my $open = ($r->{close} eq ']') ? '(': '[';
  197       push(@union,$self->Package("Interval")->make($context,$open,$d,$b,$l->{close}));
  198     }
  199   }
  200   return @union;
  201 }
  202 
  203 #
  204 #  Lexicographic order, but with type of endpoint included
  205 #    in the test.
  206 #
  207 sub compare {
  208   my ($self,$l,$r) = Value::checkOpOrder(@_);
  209   my ($la,$lb) = $l->value; my ($ra,$rb) = $r->value;
  210   my $cmp = $la <=> $ra; return $cmp if $cmp;
  211   my $ignoreEndpointTypes = $l->getFlag('ignoreEndpointTypes');
  212   $cmp = $l->{open} cmp $r->{open}; return $cmp if $cmp && !$ignoreEndpointTypes;
  213   $cmp = $lb <=> $rb; return $cmp if $cmp || $ignoreEndpointTypes;
  214   return $l->{close} cmp $r->{close};
  215 }
  216 
  217 ############################################
  218 #
  219 #  Utility routines
  220 #
  221 
  222 sub reduce {shift}
  223 sub isReduced {1}
  224 sub sort {shift}
  225 
  226 
  227 #
  228 #  Tests for containment, subsets, etc.
  229 #
  230 
  231 sub contains {
  232   my $self = shift; my $other = $self->promote(shift);
  233   return ($other - $self)->isEmpty;
  234 }
  235 
  236 sub isSubsetOf {
  237   my $self = shift; my $other = $self->promote(shift);
  238   return $other->contains($self);
  239 }
  240 
  241 sub isEmpty {0}
  242 
  243 sub intersect {
  244   my $self = shift; my $other = shift;
  245   return $self-($self-$other);
  246 }
  247 
  248 sub intersects {
  249   my $self = shift; my $other = shift;
  250   return !$self->intersect($other)->isEmpty;
  251 }
  252 
  253 ###########################################################################
  254 
  255 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9