########################################################################### package Value::Union; my $pkg = 'Value::Union'; use strict; use vars qw(@ISA); @ISA = qw(Value); use overload '+' => \&add, '.' => \&Value::_dot, 'x' => \&Value::cross, '<=>' => \&compare, 'cmp' => \&Value::cmp, 'nomethod' => \&Value::nomethod, '""' => \&Value::stringify; # # Convert a value to a union of intervals. The value must be # a list of two or more Interval, Union or Point objects. # Points will be converted to intervals if they are length 1 or 2. # sub new { my $self = shift; my $class = ref($self) || $self; @_ = split("U",@_[0]) if scalar(@_) == 1 && !ref($_[0]); Value::Error("Unions must be of at least two intervals") unless scalar(@_) > 1; my @intervals = (); my $isFormula = 0; foreach my $xx (@_) { my $x = $xx; $x = Value::Interval->new($x) if !ref($x); if (Value::isFormula($x)) { $x->{tree}->typeRef->{name} = 'Interval' if ($x->type eq 'Point' && $x->length == 1); if ($x->type eq 'Interval') {push(@intervals,$x)} elsif ($x->type eq 'Union') {push(@intervals,$x->{tree}->makeUnion)} else {Value::Error("Unions can be taken only for Intervals")} $isFormula = 1; } else { if (Value::class($x) eq 'Point' || Value::class($x) eq 'List') { if ($x->length == 1) {$x = Value::Interval->new('[',$x->value,$x->value,']')} elsif ($x->length == 2) {$x = Value::Interval->new($x->{open},$x->value,$x->{close})} } if (Value::class($x) eq 'Interval') {push(@intervals,$x)} elsif (Value::class($x) eq 'Union') {push(@intervals,@{$x->{data}})} else {Value::Error("Unions can be taken only for Intervals")} } } return $self->formula(@intervals) if $isFormula; bless {data => [@intervals], canBeInterval => 1}, $class; } # # Return the appropriate data. # sub length {return scalar(@{shift->{data}})} sub typeRef { my $self = shift; return Value::Type($self->class, $self->length, $self->data->[0]->typeRef); } # # Recursively convert the list of intervals to a tree of unions # sub formula { my $selft = shift; my $formula = Value::Formula->blank; $formula->{tree} = recursiveUnion($formula,Value::toFormula($formula,@_)); # return $formula->eval if scalar(%{$formula->{variables}}) == 0; return $formula } sub recursiveUnion { my $formula = shift; my $right = pop(@_); return $right if (scalar(@_) == 0); return $formula->{context}{parser}{BOP}-> new($formula,'U',recursiveUnion($formula,@_),$right); } ############################################ # # Operations on unions # # # Addition forms additional unions # sub add { my ($l,$r,$flag) = @_; if ($l->promotePrecedence($r)) {return $r->add($l,!$flag)} if ($flag) {my $tmp = $l; $l = $r; $r = $tmp} Value::Error("Unions can only be added to Intervals or Unions") unless Value::class($l) =~ m/Interval|Union/ && Value::class($r) =~ m/Interval|Union/; $l = $pkg->make($l) if ($l->class eq 'Interval'); $r = $pkg->make($r) if ($r->class eq 'Interval'); return $pkg->make(@{$l->data},@{$r->data}); } sub dot {add(@_)} # # @@@ Needs work @@@ # # Sort the intervals lexicographically, and then # compare interval by interval. # sub compare { my ($l,$r,$flag) = @_; if ($flag) {my $tmp = $l; $l = $r; $r = $tmp}; return 1 if Value::class($r) ne 'Union'; return -1 if Value::class($l) ne 'Union'; my @l = sort(@{$l->data}); my @r = sort(@{$r->data}); return scalar(@l) <=> scalar(@r) unless scalar(@l) == scalar(@r); my $cmp = 0; foreach my $i (0..$#l) { $cmp = $l[$i] <=> $r[$i]; last if $cmp; } return $cmp; } # @@@ simplify (combine intervals, if possible) @@@ ############################################ # # Generate the various output formats # sub string { my $self = shift; my $equation = shift; my $context = $equation->{context} || $$Value::context; my $union = $context->{operators}{'U'}{string} || ' U '; my @intervals = (); foreach my $x (@{$self->data}) {push(@intervals,$x->string($equation))} return join($union,@intervals); } sub TeX { my $self = shift; my $equation = shift; my $context = $equation->{context} || $$Value::context; my @intervals = (); my $op = $context->{operators}{'U'}; foreach my $x (@{$self->data}) {push(@intervals,$x->TeX($equation))} return join($op->{TeX} || $op->{string} || ' U ',@intervals); } ########################################################################### 1;