File Coverage

blib/lib/Number/Interval.pm
Criterion Covered Total %
statement 231 263 87.8
branch 171 240 71.2
condition 92 132 69.7
subroutine 24 26 92.3
pod 17 17 100.0
total 535 678 78.9


line stmt bran cond sub pod time code
1             package Number::Interval;
2              
3             =head1 NAME
4              
5             Number::Interval - Implement a representation of a numeric interval
6              
7             =head1 SYNOPSIS
8              
9             use Number::Interval;
10              
11             $i = new Number::Interval( Min => -4, Max => 20);
12             $i = new Number::Interval( Min => 0 );
13              
14             $is = $i->contains( $value );
15             $status = $i->intersection( $i2 );
16              
17             print "$i";
18              
19             =head1 DESCRIPTION
20              
21             Simple class to implement a closed or open interval. Can be used to
22             compare different intervals, determine set membership, calculate
23             intersections and provide default stringification methods.
24              
25             Intervals can be bound or unbound. If C is less than C
26             the interval is inverted.
27              
28             =cut
29              
30 1     1   1454 use 5.006;
  1         4  
  1         44  
31 1     1   6 use strict;
  1         2  
  1         43  
32 1     1   16 use warnings;
  1         2  
  1         45  
33 1     1   5 use Carp;
  1         2  
  1         78  
34 1     1   5689 use Data::Dumper;
  1         8051  
  1         129  
35             use overload
36 1         13 '""' => "stringify",
37             '==' => 'equate',
38             'eq' => "equate",
39             '!=' => "notequal",
40 1     1   11 'ne' => "notequal";
  1         2  
41              
42             # CVS ID: $Id$
43              
44 1     1   123 use vars qw/ $VERSION /;
  1         2  
  1         2919  
45             $VERSION = '0.06';
46              
47             # hash of allowed lower-cased constructor keys with
48             # corresponding accessor method
49             my %ConstructAllowed = (
50             min => 'min',
51             max => 'max',
52             incmax => 'inc_max',
53             incmin => 'inc_min',
54             posdef => 'pos_def',
55             );
56              
57             =head1 METHODS
58              
59             =head2 Constructor
60              
61             =over 4
62              
63             =item B
64              
65             Create a new object. Can be populated when supplied with
66             keys C and C.
67              
68             $r = new Number::Interval();
69              
70             This interval is Inf.
71              
72             $r = new Number::Interval( Max => 5 );
73              
74             This interval is > 5.
75              
76             $r = new Number::Interval( Max => 5, Min => 22 );
77              
78             This interval is > 22 and < 5.
79              
80             By default the interval does not include the bounds themselves. They
81             can be included by using the IncMax and IncMin keys.
82              
83             $r = new Number::Interval( Max => 5, IncMax => 1 );
84              
85             The above interval is >=5
86              
87             Positive-definite intervals allow the stringification to ignore
88             the lower bound if it is 0 (even if set explicitly).
89              
90             $r = new Number::Interval( Max => 5, IncMax => 1, Min => 0,
91             PosDef => 1);
92              
93             The keys are case-insensitive.
94              
95             =cut
96              
97             sub new {
98 101     101 1 3514 my $proto = shift;
99 101   66     477 my $class = ref($proto) || $proto;
100              
101 101         262 my %args = @_;
102              
103 101         415 my $r = {
104             Min => undef,
105             Max => undef,
106             IncMax => 0,
107             IncMin => 0,
108             PosDef => 0,
109             };
110              
111             # Create object
112 101         209 my $obj = bless $r, $class;
113              
114             # Populate it
115 101         220 for my $key (keys %args) {
116 167         263 my $lc = lc( $key );
117 167 50       333 if (exists $ConstructAllowed{$lc}) {
118 167         207 my $method = $ConstructAllowed{$lc};
119 167         377 $obj->$method( $args{$key} );
120             }
121             }
122              
123 101         339 return $obj;
124             }
125              
126             =item B
127              
128             Copy the contents of the current object into a new object and return it.
129              
130             $new = $r->copy;
131              
132             =cut
133              
134             sub copy {
135 1     1 1 3 my $self = shift;
136 1         5 my $new = $self->new();
137             # simplistic hash copy since we know that we are a simple hash internally
138             # subclasses might get in trouble if they have complexity.
139 1         7 %$new = %$self;
140 1         4 return $new;
141             }
142              
143             =back
144              
145             =head2 Accessors
146              
147             =over 4
148              
149             =item B
150              
151             Return (or set) the upper end of the interval.
152              
153             $max = $r->max;
154             $r->max(22.0);
155              
156             C indicates that the interval has no upper bound.
157              
158             =cut
159              
160             sub max {
161 482     482 1 531 my $self = shift;
162 482 100       994 $self->{Max} = shift if @_;
163 482         935 return $self->{Max};
164             }
165              
166             =item B
167              
168             Return (or set) the lower end of the interval.
169              
170             $min = $r->min;
171             $r->min( undef );
172              
173             C indicates that the interval has no lower bound.
174              
175             =cut
176              
177             sub min {
178 513     513 1 1497 my $self = shift;
179 513 100       973 $self->{Min} = shift if @_;
180 513         1121 return $self->{Min};
181             }
182              
183             =item B
184              
185             Return (or set) the boolean indicating whether the maximum bound
186             of the interval should be included in the bound definition. If true,
187             the bounds will be >= max.
188              
189             $inc = $r->inc_max;
190             $r->inc_max( 1 );
191              
192             Default is false (not included).
193              
194             =cut
195              
196             sub inc_max {
197 179     179 1 191 my $self = shift;
198 179 100       310 $self->{IncMax} = shift if @_;
199 179         488 return $self->{IncMax};
200             }
201              
202              
203             =item B
204              
205             Return (or set) the boolean indicating whether the minimum bound
206             of the interval should be included in the bound definition. If true,
207             the bounds will be <= min.
208              
209             $inc = $r->inc_min;
210             $r->inc_min( 1 );
211              
212             Default is false (not included).
213              
214             =cut
215              
216             sub inc_min {
217 168     168 1 176 my $self = shift;
218 168 100       312 $self->{IncMin} = shift if @_;
219 168         401 return $self->{IncMin};
220             }
221              
222             =item B
223              
224             Indicate that the interval is positive definite. This helps the
225             stringification method to determine whether the lower bound
226             should be included
227              
228             $r->pos_def( 1 );
229              
230              
231             If set to true, automatically sets the lower bound to 0 if the lower bound
232             is not explicitly defined.
233              
234             =cut
235              
236             sub pos_def {
237 20     20 1 426 my $self = shift;
238 20 100       40 if (@_) {
239 1         3 $self->{PosDef} = shift;
240 1 50 33     7 if ($self->{PosDef} && !defined $self->min) {
241 1         3 $self->min( 0 );
242             }
243             }
244 20         79 return $self->{PosDef};
245             }
246              
247             =item B
248              
249             Return (or set) the minimum and maximum values of the
250             interval as an array.
251              
252             $r->minmax( 1, 5 );
253             @interval = $r->minmax;
254              
255             Returns reference to an array in a scalar context.
256              
257             =cut
258              
259             sub minmax {
260 3     3 1 1017 my $self = shift;
261 3 100       10 if (@_) {
262 2         6 $self->min( $_[0] );
263 2         6 $self->max( $_[1] );
264             }
265 3         26 my @minmax = ( $self->min, $self->max );
266 3 100       13 return (wantarray ? @minmax : \@minmax);
267             }
268              
269             =item B
270              
271             Return (or set) the minimum and maximum values of the
272             interval as an hash.
273              
274             $r->minmax_hash( min => 1, max => 5 );
275             %interval = $r->minmax_hash;
276              
277             Returns reference to an hash in a scalar context.
278              
279             C or C can be ommitted. The returned hash
280             contains C and C keys but only if they
281             have defined values.
282              
283             =cut
284              
285             sub minmax_hash {
286 0     0 1 0 my $self = shift;
287 0 0       0 if (@_) {
288 0         0 my %args = @_;
289 0 0       0 $self->min( $args{min} ) if exists $args{min};
290 0 0       0 $self->max( $args{max} ) if exists $args{max};
291             }
292              
293             # Populate the output hash
294 0         0 my %minmax;
295 0 0       0 $minmax{min} = $self->min if defined $self->min;
296 0 0       0 $minmax{max} = $self->max if defined $self->max;
297              
298 0 0       0 return (wantarray ? %minmax : \%minmax);
299             }
300              
301             =item B
302              
303             Returns the size of the interval.
304              
305             $sizeof = $r->sizeof;
306              
307             If either of the lower or upper ends are unbounded, then C will
308             be returned.
309              
310             =cut
311              
312             sub sizeof {
313 5     5 1 83 my $self = shift;
314 5 100 66     12 if( ! defined( $self->min ) ||
315             ! defined( $self->max ) ) {
316 3         16 return undef;
317             }
318              
319 2         6 return abs( $self->max - $self->min );
320             }
321              
322             =back
323              
324             =head2 General
325              
326             =over 4
327              
328             =item B
329              
330             Convert the object into a string representation for display.
331             Usually called via a stringify overload.
332              
333             =cut
334              
335             sub stringify {
336 6     6 1 26 my $self = shift;
337              
338 6         15 my $min = $self->min;
339 6         23 my $max = $self->max;
340              
341             # are we inclusive (for unbound ranges)
342 6 100       15 my $inc_min_ub = ( $self->inc_min ? "=" : " " );
343 6 50       13 my $inc_max_ub = ( $self->inc_max ? "=" : " " );
344              
345 6 50 33     34 if (defined $min && defined $max) {
    0          
    0          
346             # Bound
347              
348             # use standard interval notation when using a bound range
349 6 100       13 my $inc_min_b = ( $self->inc_min ? "[" : "(" );
350 6 50       15 my $inc_max_b = ( $self->inc_max ? "]" : ")" );
351              
352 6 100       23 if ($min == $max) {
    100          
353             # no range
354 1         6 return "==$min";
355             } elsif ($max < $min) {
356 1         7 return "<$inc_max_ub$max and >$inc_min_ub$min";
357             } else {
358 4 100 66     22 if ($min <= 0 && $self->pos_def) {
359 2         16 return "<$inc_max_ub$max";
360             } else {
361 2         643 return "$inc_min_b$min,$max$inc_max_b";
362             }
363             }
364             } elsif (defined $min) {
365 0         0 return ">$inc_min_ub$min";
366             } elsif (defined $max) {
367 0         0 return "<$inc_max_ub$max";
368             } else {
369 0         0 return "Inf";
370             }
371              
372             }
373              
374             =item B
375              
376             Determine whether the interval is inverted. This is true if
377             both max and min are supplied but max is less than min. For all other
378             cases (including unbound single-sided intervals) this will return false.
379              
380             =cut
381              
382             sub isinverted {
383 114     114 1 122 my $self = shift;
384 114         170 my $min = $self->min;
385 114         209 my $max = $self->max;
386              
387 114 100 100     411 if (defined $min and defined $max) {
388 74 100       161 return 1 if $min > $max;
389             }
390 92         153 return 0;
391             }
392              
393             =item B
394              
395             Returns true if the interval is bound by an upper and lower limit.
396             An inverted interval would be bounded but inverted.
397              
398             =cut
399              
400             sub isbound {
401 84     84 1 105 my $self = shift;
402 84         132 my $min = $self->min;
403 84         133 my $max = $self->max;
404 84 100 100     281 if (defined $min and defined $max) {
405 48         81 return 1;
406             } else {
407 36         55 return 0;
408             }
409             }
410              
411              
412             =item B
413              
414             Compare with another Interval object.
415             Returns true if they are the same. False otherwise.
416              
417             =cut
418              
419             sub equate {
420 9     9 1 159 my $self = shift;
421 9         12 my $comparison = shift;
422              
423             # Need to check that both are objects
424 9 50       18 return 0 unless defined $comparison;
425 9 50       34 return 0 unless UNIVERSAL::isa($comparison, "Number::Interval");
426              
427             # need to be explicit about undefs
428             # return false immediately we find a difference
429 9         17 for my $m (qw/ min max/) {
430             # first values
431 15 100       34 if ( defined $comparison->$m() ) {
432 9 50       16 return 0 if !defined $self->$m();
433 9 100       18 return 0 if $comparison->$m() != $self->$m();
434             } else {
435 6 100       14 return 0 if defined $self->$m();
436             }
437              
438             # then boolean
439 13         23 my $incm = 'inc_' . $m;
440              
441             # return false if state of one is NOT the other
442 13 100 33     25 return 0 if ( ( $self->$incm() && !$comparison->$incm() ) ||
      66        
      33        
443             ( !$self->$incm() && $comparison->$incm() ) );
444             }
445 5         29 return 1;
446             }
447              
448             =item B
449              
450             Inverse of C. Used by the tied interface to implement !=.
451              
452             $i1 != $i2
453              
454             =cut
455              
456             sub notequal {
457 4     4 1 23 my $self = shift;
458 4         11 return !$self->equate( @_ );
459             }
460              
461             =item B
462              
463             Determine whether a supplied value is within the defined intervals.
464              
465             $is = $i->contains( $value );
466              
467             If both intervals are undefined, always returns true.
468              
469             If the min == max, returns true if the supplied value is that
470             value, regardless of IncMin and IncMax setttings.
471              
472             If the interval is positive definite, always returns false if the
473             supplied value is negative.
474              
475             =cut
476              
477             sub contains {
478 28     28 1 3841 my $self = shift;
479 28         31 my $value = shift;
480              
481 28         54 my $max = $self->max;
482 28         52 my $min = $self->min;
483 28 50 66     61 return 1 if (!defined $max && !defined $min);
484              
485             # Assume it doesnt match the interval
486 28         29 my $contains = 0;
487 28 100       49 if ($self->isinverted) {
488             # Inverted interval. Both max and min must be defined
489 6 50 33     19 if (defined $max and defined $min) {
490 6 50 33     11 if ($self->inc_max && $self->inc_min) {
    50          
    50          
491 0 0 0     0 if ($value <= $max || $value >= $min) {
492 0         0 $contains = 1;
493             }
494             } elsif ($self->inc_max) {
495 0 0 0     0 if ($value <= $max || $value > $min) {
496 0         0 $contains = 1;
497             }
498             } elsif ($self->inc_min) {
499 0 0 0     0 if ($value < $max || $value >= $min) {
500 0         0 $contains = 1;
501             }
502             } else {
503 6 100 100     24 if ($value < $max || $value > $min) {
504 4         6 $contains = 1;
505             }
506             }
507              
508             } else {
509 0         0 croak "An interval can not be inverted with only one defined value";
510             }
511              
512             } else {
513             # normal interval
514 22 100 100     85 if (defined $max and defined $min) {
    100          
    50          
515 18 100 100     44 if ($max == $min) { # need to include a bound
    100 33        
    50          
    50          
    100          
516 3 100 66     8 if ($self->inc_min || $self->inc_max) {
517 2 100       6 $contains = 1 if $value == $max;
518             }
519             } elsif ($self->pos_def && $value < 0) {
520 2         4 $contains = 0;
521             } elsif ($self->inc_max && $self->inc_min) {
522 0 0 0     0 if ($value <= $max && $value >= $min) {
523 0         0 $contains = 1;
524             }
525             } elsif ($self->inc_max) {
526 0 0 0     0 if ($value <= $max && $value > $min) {
527 0         0 $contains = 1;
528             }
529             } elsif ($self->inc_min) {
530 4 100 100     19 if ($value < $max && $value >= $min) {
531 2         4 $contains = 1;
532             }
533             } else {
534 9 100 100     49 if ($value < $max && $value > $min) {
535 5         7 $contains = 1;
536             }
537             }
538             } elsif (defined $max) {
539 2 50       4 if ($self->inc_max) {
540 0 0       0 $contains = 1 if $value <= $max;
541             } else {
542 2 100       7 $contains = 1 if $value <= $max;
543             }
544             } elsif (defined $min) {
545 2 50       4 if ($self->inc_min) {
546 0 0       0 $contains = 1 if $value >= $min;
547             } else {
548 2 100       5 $contains = 1 if $value > $min;
549             }
550             }
551             }
552              
553 28         103 return $contains;
554             }
555              
556              
557             =item B
558              
559             Given another Interval object, modify the existing interval to include
560             the additional constraints. For example, if the current object
561             has a interval of -3 to 10, and it is merged with an external object
562             that has a interval of 0 to 20 then the interval of the current object
563             will be converted to 0 to 10 since that is consistent with both
564             intervals.
565              
566             $status = $interval->intersection( $newinterval );
567              
568             Returns true if the intersection was successful. If the intervals are
569             incompatible (no intersection) or if no object was supplied returns
570             false and the object is not modified.
571              
572             Intersections of an inverted interval with a non-inverted interval
573             can, in some circumstances, result in an intersection covering
574             two distinct bound intervals. This class can not yet support multiple
575             intervals (that would make the intersection method even more of a nightmare)
576             so the routine dies if such a situation arises.
577              
578             =cut
579              
580             # There must be a neater way of implementing this method!
581             # There may be some edge cases that fail (when one of the
582             # interval boundaries is identical in both objects)
583              
584             sub intersection {
585 42     42 1 175 my $self = shift;
586 42         40 my $new = shift;
587              
588             # Check input
589 42 50       79 return 0 unless defined $new;
590 42 50       176 return 0 unless UNIVERSAL::isa($new,"Number::Interval");
591              
592             # Get the values
593 42         71 my $max1 = $self->max;
594 42         70 my $min1 = $self->min;
595 42         72 my $max2 = $new->max;
596 42         76 my $min2 = $new->min;
597              
598 42         91 my $inverted1 = $self->isinverted;
599 42         74 my $inverted2 = $new->isinverted;
600 42   66     110 my $inverted = $inverted1 || $inverted2;
601              
602 42         85 my $bound1 = $self->isbound;
603 42         71 my $bound2 = $new->isbound;
604 42   100     102 my $bound = $bound1 || $bound2;
605              
606 42         46 my $outmax;
607             my $outmin;
608              
609             # There are six possible combinations of Bound interval,
610             # inverted interval and unbound interval.
611              
612 42 100       59 if ($bound) {
613             # Support BB, BU and BI and II
614              
615 33 100       56 if ($inverted) {
616             # Any inverted: II or BI or IB or UI or IU
617             #print "*********** INVERTED *********\n";
618              
619 14 100 66     56 if ($inverted1 && $inverted2) {
620             # II
621             # This is fairly easy.
622             # Always take the smallest max and largest min
623 1 50       3 $outmin = ( $min1 > $min2 ? $min1 : $min2);
624 1 50       6 $outmax = ( $max1 < $max2 ? $max1 : $max2);
625              
626             } else {
627             # IB, IU (BI and UI)
628             # swap if needed, to have everything as IX
629 13         19 my $nowbound;
630 13 50       23 if ($inverted2) {
631 0         0 ($max1,$min1,$max2,$min2) = ($max2,$min2,$max1,$min1);
632             # determine bound state of #1 before losing order information
633 0         0 $nowbound = $bound1;
634             } else {
635             # #1 is inverted so we need the bound state of #1
636 13         19 $nowbound = $bound2;
637             }
638              
639 13 100       23 if ($nowbound) {
640             # IB
641             # We know that max2 and min2 are defined
642             # We always end up with at least one bound interval
643 6 100       17 if ($min2 < $max1) {
    100          
    50          
644 3         4 $outmin = $min2;
645              
646             # If max2 is too high we get two intervals.
647 3 100       184 croak "This intersection results in two output intervals. Currently not supported" if $max2 > $min1;
648              
649             # Upper limit of interval must be the min of the two maxes
650 2 100       6 $outmax = ( $max1 < $max2 ? $max1 : $max2 );
651              
652             } elsif ($min2 < $min1) {
653              
654             # Make sure we intersect a little
655             # If the bound interval lies outside the inverted interval
656             # return undef
657 2 100       6 if ($max2 >= $min1) {
658 1         2 $outmin = $min1;
659 1         1 $outmax = $max2;
660             }
661              
662             } elsif ($min2 > $min1) {
663              
664             # This is just the bound interval
665 1         1 $outmin = $min2;
666 1         2 $outmax = $max2;
667              
668              
669             } else {
670 0         0 croak "Oops Bug in interval intersection [6]\n".
671             _formaterr( $min1, $max1, $min2, $max2);
672             }
673              
674              
675             } else {
676             # IU
677 7 100       23 if (defined $max2) {
    100          
678              
679             # The upper bound must be below the inverted "min"
680             # else we get intersection of two intervals
681 3 100       15 if ($max2 > $min1) {
    100          
682 1         165 croak "This intersection results in two output intervals. Currently not supported";
683             } elsif ($max2 > $max1) {
684             # Just use the inverted interval
685 1         3 $outmax = $max1;
686 1         3 $outmin = $min1;
687             } else {
688             # max must be decreased to include min2
689 1         2 $outmax = $max2;
690 1         3 $outmin = $min1;
691             }
692              
693              
694             } elsif (defined $min2) {
695              
696             # The lower bound must be above the "max"
697             # else we get an intersection of two intervals
698 3 100       20 if ($min2 < $max1) {
    100          
699 1         151 croak "This intersection results in two output intervals. Currently not supported";
700             } elsif ($min2 < $min1) {
701             # Just use the inverted interval
702 1         2 $outmax = $max1;
703 1         3 $outmin = $min1;
704             } else {
705             # min must be increased to include min2
706 1         2 $outmax = $max1;
707 1         3 $outmin = $min2;
708             }
709              
710             } else {
711             # both undefined
712 1         3 $outmax = $max1;
713 1         3 $outmin = $min1;
714             }
715              
716             }
717              
718             }
719              
720              
721              
722             } else {
723             # BB, BU or UB
724             #print "*********** BOUND NON INVERTED ************\n";
725 19 100 100     63 if ($bound1 and $bound2) {
726             # BB
727             #print "---------- BB -----------\n";
728 8 50       45 $outmin = ( $min1 > $min2 ? $min1 : $min2 );
729 8 100       14 $outmax = ( $max1 < $max2 ? $max1 : $max2 );
730              
731             # Check that we really are overlapping
732 8 100       26 if ($outmax < $outmin) {
733             # oops - intervals did not intersect. Reset
734 1         2 $outmin = $outmax = undef;
735             }
736            
737              
738             } else {
739             # BU and UB
740             #print "---------- BU/UB -----------\n";
741             # swap if needed, to have everything as BU
742 11 100       18 if ($bound2) {
743 4         9 ($max1,$min1,$max2,$min2) = ($max2,$min2,$max1,$min1);
744             }
745              
746             # unbound is now guaranteed to be (2)
747             # Check that unbound max is in interval
748 11 100       28 if (defined $max2) {
    100          
749 4 100 66     23 if ($max2 <= $max1 && $max2 >= $min1) {
    50          
    50          
750             # inside interval
751 2         2 $outmax = $max2;
752 2         4 $outmin = $min1;
753             } elsif ($max2 <= $min1) {
754             # outside interval. No intersection
755             } elsif ($max2 >= $max1) {
756             # below interval. irrelevant
757 2         3 $outmax = $max1;
758 2         3 $outmin = $min1;
759             } else {
760 0         0 croak "Number::Interval - This should not happen[2]\n".
761             _formaterr( $min1, $max1, $min2, $max2);
762             }
763              
764             } elsif (defined $min2) {
765 5 100 100     23 if ($min2 <= $max1 && $min2 >= $min1) {
    100          
    50          
766             # inside interval
767 3         3 $outmax = $max1;
768 3         25 $outmin = $min2;
769             } elsif ($min2 >= $max1) {
770             # outside interval. No intersection
771             } elsif ($min2 <= $min1) {
772             # below interval. irrelevant
773 1         2 $outmax = $max1;
774 1         2 $outmin = $min1;
775             } else {
776 0         0 croak "Number::Interval - This should not happen[3]:\n" .
777             _formaterr( $min1, $max1, $min2, $max2);
778             }
779              
780             } else {
781             # The second interval is unbounded at both ends
782 2         4 $outmax = $max1;
783 2         4 $outmin = $min1;
784             }
785              
786              
787             }
788              
789             }
790              
791              
792             } else {
793             # Unbound+Unbound only
794             # Four options here.
795             # 1. A max and a max => max (same for min and min)
796             # 2. max and a min with no overlap => no intersection
797             # 3. max and min with overlap => bounded interval
798             # 4. all undefined
799 9 100 100     54 if (defined $max1 && defined $max2) {
    100 100        
800 1 50       4 $outmax = ( $max1 < $max2 ? $max1 : $max2 );
801             } elsif (defined $min2 && defined $min1) {
802 1 50       4 $outmin = ( $min1 > $min2 ? $min1 : $min2 );
803             } else {
804             # max and a min - one must be defined for both
805 7 100       16 my $refmax = (defined $max1 ? $max1 : $max2);
806 7 100       11 my $refmin = (defined $min1 ? $min1 : $min2);
807              
808 7 100 100     54 if (!defined $refmax && !defined $refmin) {
    100          
    100          
    100          
809             # infinite bound
810 1         6 return 1;
811             } elsif (!defined $refmax) {
812             # just a min
813 1         2 $outmin = $refmin;
814             } elsif (!defined $refmin) {
815             # just a max
816 1         2 $outmax = $refmax;
817             } elsif ($refmax > $refmin) {
818             # normal bound interval
819 2         3 $outmax = $refmax;
820 2         5 $outmin = $refmin;
821             } else {
822             # unbound interval. No intersection
823             }
824              
825              
826             }
827              
828             }
829              
830              
831             # Modify object if we have new values
832 38 100 100     110 if (defined $outmax or defined $outmin) {
833             # Need to check the inc_min and inc_max settings
834 33         71 my $inc_max = $self->_checkinc( $outmax, $max1, $max2,
835             $self->inc_max, $new->inc_max );
836 33         80 my $inc_min = $self->_checkinc( $outmin, $min1, $min2,
837             $self->inc_min, $new->inc_min );
838              
839             # Abort if the min and max are the same and we
840             # are not including the bounds in the interval
841 33 100 100     264 if (defined $outmax && defined $outmin &&
      100        
      66        
      100        
842             $outmax == $outmin &&
843             (!$inc_max || !$inc_min)
844             ) {
845 2         12 return 0;
846             }
847              
848 31         62 $self->inc_min( $inc_min );
849 31         58 $self->inc_max( $inc_max );
850 31         51 $self->max($outmax);
851 31         56 $self->min($outmin);
852 31         141 return 1;
853             } else {
854 5         20 return 0;
855             }
856              
857             }
858              
859             # Given
860             sub _checkinc {
861 66     66   69 my $self = shift;
862 66         65 my $newval = shift;
863 66         57 my $ref1 = shift;
864 66         64 my $ref2 = shift;
865 66         57 my $inc1 = shift;
866 66         115 my $inc2 = shift;
867              
868 66         69 my $inc_val = $inc1;
869 66 100       117 if (defined $newval) {
870 62 100 100     494 if (defined $ref1 && $ref1 == $newval &&
    100 100        
      100        
      100        
871             defined $ref2 && $ref2 == $newval) {
872             # value comes from both so we want the least
873             # inclusive inc_max value
874 4 50 66     48 $inc_val = 0 if (!$inc1 || !$inc2);
875             } elsif (defined $ref2 && $ref2 == $newval) {
876             # this value comes from ref2 so we copy
877             # inc from #2
878 23         29 $inc_val = $inc2;
879             }
880             }
881 66         110 return $inc_val;
882             }
883              
884             sub _formaterr {
885 0     0     my ($min1, $max1, $min2, $max2) = @_;
886 0 0         return "Comparing : (".
    0          
    0          
    0          
887             (defined $min1 ? $min1 : "" ).
888             "," .
889             (defined $max1 ? $max1 : "" ).
890             ") with (".
891             (defined $min2 ? $min2 : "" ).
892             "," . (defined $max2 ? $max2 : "" ).
893             ")";
894             }
895              
896             =back
897              
898             =head1 NOTES
899              
900             The default interval is not inclusive of the bounds.
901              
902             =head1 COPYRIGHT
903              
904             Copyright (C) 2009-2011 Science and Technology Facilities Council.
905             Copyright (C) 2002-2005 Particle Physics and Astronomy Research Council.
906             All Rights Reserved.
907              
908             This program is free software; you can redistribute it and/or modify it under
909             the terms of the GNU General Public License as published by the Free Software
910             Foundation; either version 2 of the License, or (at your option) any later
911             version.
912              
913             This program is distributed in the hope that it will be useful,but WITHOUT ANY
914             WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
915             PARTICULAR PURPOSE. See the GNU General Public License for more details.
916              
917             You should have received a copy of the GNU General Public License along with
918             this program; if not, write to the Free Software Foundation, Inc., 59 Temple
919             Place,Suite 330, Boston, MA 02111-1307, USA
920              
921             =head1 AUTHOR
922              
923             Tim Jenness Etjenness@cpan.orgE.
924              
925             =cut
926              
927             1;
928