File Coverage

blib/lib/Set/Infinite/Basic.pm
Criterion Covered Total %
statement 471 564 83.5
branch 182 276 65.9
condition 52 79 65.8
subroutine 52 62 83.8
pod 4 42 9.5
total 761 1023 74.3


line stmt bran cond sub pod time code
1             package Set::Infinite::Basic;
2              
3             # Copyright (c) 2001, 2002, 2003 Flavio Soibelmann Glock. All rights reserved.
4             # This program is free software; you can redistribute it and/or
5             # modify it under the same terms as Perl itself.
6              
7             require 5.005_03;
8 11     11   90 use strict;
  11         22  
  11         486  
9              
10             require Exporter;
11 11     11   69 use Carp;
  11         20  
  11         1094  
12 11     11   13750 use Data::Dumper;
  11         145820  
  11         903  
13 11     11   99 use vars qw( @ISA @EXPORT_OK @EXPORT );
  11         23  
  11         1086  
14 11     11   57 use vars qw( $Type $tolerance $fixtype $inf $minus_inf @Separators $neg_inf );
  11         22  
  11         1862  
15              
16             @ISA = qw(Exporter);
17             @EXPORT_OK = qw( INFINITY NEG_INFINITY );
18             @EXPORT = qw();
19              
20 11     11   67 use constant INFINITY => 100**100**100;
  11         24  
  11         853  
21 11     11   154 use constant NEG_INFINITY => - INFINITY;
  11         20  
  11         1012  
22              
23             $inf = INFINITY;
24             $minus_inf = $neg_inf = NEG_INFINITY;
25              
26             use overload
27 11         101 '<=>' => \&spaceship,
28             qw("" as_string),
29 11     11   164 ;
  11         25  
30              
31              
32             # TODO: make this an object _and_ class method
33             # TODO: POD
34             sub separators {
35 30086     30086 0 30171 shift;
36 30086 100       109409 return $Separators[ $_[0] ] if $#_ == 0;
37 14 50       108 @Separators = @_ if @_;
38 14         26 return @Separators;
39             }
40              
41             BEGIN {
42 11     11   1883 __PACKAGE__->separators (
43             '[', ']', # a closed interval
44             '(', ')', # an open interval
45             '..', # number separator
46             ',', # list separator
47             '', '', # set delimiter '{' '}'
48             );
49             # global defaults for object private vars
50 11         13 $Type = undef;
51 11         21 $tolerance = 0;
52 11         72051 $fixtype = 1;
53             }
54              
55             # _simple_* set of internal methods: basic processing of "spans"
56              
57             sub _simple_intersects {
58 215     215   301 my $tmp1 = $_[0];
59 215         252 my $tmp2 = $_[1];
60 215         229 my ($i_beg, $i_end, $open_beg, $open_end);
61 215         454 my $cmp = $tmp1->{a} <=> $tmp2->{a};
62 215 50       386 if ($cmp < 0) {
    0          
63 215         266 $i_beg = $tmp2->{a};
64 215         326 $open_beg = $tmp2->{open_begin};
65             }
66             elsif ($cmp > 0) {
67 0         0 $i_beg = $tmp1->{a};
68 0         0 $open_beg = $tmp1->{open_begin};
69             }
70             else {
71 0         0 $i_beg = $tmp1->{a};
72 0   0     0 $open_beg = $tmp1->{open_begin} || $tmp2->{open_begin};
73             }
74 215         411 $cmp = $tmp1->{b} <=> $tmp2->{b};
75 215 50       665 if ($cmp > 0) {
    100          
76 0         0 $i_end = $tmp2->{b};
77 0         0 $open_end = $tmp2->{open_end};
78             }
79             elsif ($cmp < 0) {
80 214         296 $i_end = $tmp1->{b};
81 214         342 $open_end = $tmp1->{open_end};
82             }
83             else {
84 1         2 $i_end = $tmp1->{b};
85 1   33     6 $open_end = ($tmp1->{open_end} || $tmp2->{open_end});
86             }
87 215         278 $cmp = $i_beg <=> $i_end;
88 215 100 66     2151 return 0 if
      66        
      66        
89             ( $cmp > 0 ) ||
90             ( ($cmp == 0) && ($open_beg || $open_end) ) ;
91 3         7 return 1;
92             }
93              
94              
95             sub _simple_complement {
96 1252     1252   1493 my $self = $_[0];
97 1252 100       3127 if ($self->{b} == $inf) {
98 9 100       27 return if $self->{a} == $neg_inf;
99 8         35 return { a => $neg_inf,
100             b => $self->{a},
101             open_begin => 1,
102             open_end => ! $self->{open_begin} };
103             }
104 1243 100       2986 if ($self->{a} == $neg_inf) {
105 9         42 return { a => $self->{b},
106             b => $inf,
107             open_begin => ! $self->{open_end},
108             open_end => 1 };
109             }
110 1234         11401 ( { a => $neg_inf,
111             b => $self->{a},
112             open_begin => 1,
113             open_end => ! $self->{open_begin}
114             },
115             { a => $self->{b},
116             b => $inf,
117             open_begin => ! $self->{open_end},
118             open_end => 1
119             }
120             );
121             }
122              
123             sub _simple_union {
124 2057     2057   2907 my ($tmp2, $tmp1, $tolerance) = @_;
125 2057         1935 my $cmp;
126 2057 100       3145 if ($tolerance) {
127             # "integer"
128 3 50       9 my $a1_open = $tmp1->{open_begin} ? -$tolerance : $tolerance ;
129 3 50       9 my $b1_open = $tmp1->{open_end} ? -$tolerance : $tolerance ;
130 3 50       7 my $a2_open = $tmp2->{open_begin} ? -$tolerance : $tolerance ;
131 3 50       11 my $b2_open = $tmp2->{open_end} ? -$tolerance : $tolerance ;
132             # open_end touching?
133 3 100       23 if ((($tmp1->{b}+$tmp1->{b}) + $b1_open ) <
134             (($tmp2->{a}+$tmp2->{a}) - $a2_open)) {
135             # self disjuncts b
136 2         6 return ( $tmp1, $tmp2 );
137             }
138 1 50       6 if ((($tmp1->{a}+$tmp1->{a}) - $a1_open ) >
139             (($tmp2->{b}+$tmp2->{b}) + $b2_open)) {
140             # self disjuncts b
141 0         0 return ( $tmp2, $tmp1 );
142             }
143             }
144             else {
145             # "real"
146 2054         3610 $cmp = $tmp1->{b} <=> $tmp2->{a};
147 2054 100 66     11549 if ( $cmp < 0 ||
      33        
      66        
148             ( $cmp == 0 && $tmp1->{open_end} && $tmp2->{open_begin} ) ) {
149 208         564 return ( $tmp1, $tmp2 );
150             }
151 1846         2727 $cmp = $tmp1->{a} <=> $tmp2->{b};
152 1846 100 100     14067 if ( $cmp > 0 ||
      100        
      66        
153             ( $cmp == 0 && $tmp2->{open_end} && $tmp1->{open_begin} ) ) {
154 219         915 return ( $tmp2, $tmp1 );
155             }
156             }
157              
158 1628         1913 my $tmp;
159 1628         2194 $cmp = $tmp1->{a} <=> $tmp2->{a};
160 1628 100       3483 if ($cmp > 0) {
    100          
161 305         635 $tmp->{a} = $tmp2->{a};
162 305         596 $tmp->{open_begin} = $tmp2->{open_begin};
163             }
164             elsif ($cmp == 0) {
165 1314         2762 $tmp->{a} = $tmp1->{a};
166 1314 50       2888 $tmp->{open_begin} = $tmp1->{open_begin} ? $tmp2->{open_begin} : 0;
167             }
168             else {
169 9         24 $tmp->{a} = $tmp1->{a};
170 9         17 $tmp->{open_begin} = $tmp1->{open_begin};
171             }
172              
173 1628         2391 $cmp = $tmp1->{b} <=> $tmp2->{b};
174 1628 100       3621 if ($cmp < 0) {
    100          
175 631         1030 $tmp->{b} = $tmp2->{b};
176 631         936 $tmp->{open_end} = $tmp2->{open_end};
177             }
178             elsif ($cmp == 0) {
179 652         1083 $tmp->{b} = $tmp1->{b};
180 652 100       1423 $tmp->{open_end} = $tmp1->{open_end} ? $tmp2->{open_end} : 0;
181             }
182             else {
183 345         569 $tmp->{b} = $tmp1->{b};
184 345         502 $tmp->{open_end} = $tmp1->{open_end};
185             }
186 1628         3440 return $tmp;
187             }
188              
189              
190             sub _simple_spaceship {
191 75     75   105 my ($tmp1, $tmp2, $inverted) = @_;
192 75         71 my $cmp;
193 75 50       147 if ($inverted) {
194 0         0 $cmp = $tmp2->{a} <=> $tmp1->{a};
195 0 0       0 return $cmp if $cmp;
196 0         0 $cmp = $tmp1->{open_begin} <=> $tmp2->{open_begin};
197 0 0       0 return $cmp if $cmp;
198 0         0 $cmp = $tmp2->{b} <=> $tmp1->{b};
199 0 0       0 return $cmp if $cmp;
200 0         0 return $tmp1->{open_end} <=> $tmp2->{open_end};
201             }
202 75         153 $cmp = $tmp1->{a} <=> $tmp2->{a};
203 75 100       187 return $cmp if $cmp;
204 28         52 $cmp = $tmp2->{open_begin} <=> $tmp1->{open_begin};
205 28 50       48 return $cmp if $cmp;
206 28         55 $cmp = $tmp1->{b} <=> $tmp2->{b};
207 28 100       50 return $cmp if $cmp;
208 26         52 return $tmp2->{open_end} <=> $tmp1->{open_end};
209             }
210              
211              
212             sub _simple_new {
213 8034     8034   11649 my ($tmp, $tmp2, $type) = @_;
214 8034 50       14835 if ($type) {
215 0 0       0 if ( ref($tmp) ne $type ) {
216 0         0 $tmp = new $type $tmp;
217             }
218 0 0       0 if ( ref($tmp2) ne $type ) {
219 0         0 $tmp2 = new $type $tmp2;
220             }
221             }
222 8034 50       16430 if ($tmp > $tmp2) {
223 0         0 carp "Invalid interval specification: start value is after end";
224             # ($tmp, $tmp2) = ($tmp2, $tmp);
225             }
226 8034         45164 return { a => $tmp , b => $tmp2 , open_begin => 0 , open_end => 0 };
227             }
228              
229              
230             sub _simple_as_string {
231 7687     7687   12100 my $set = shift;
232 7687         8814 my $self = $_[0];
233 7687         7756 my $s;
234 7687 50       14144 return "" unless defined $self;
235 7687 100       18437 $self->{open_begin} = 1 if ($self->{a} == -$inf );
236 7687 100       15170 $self->{open_end} = 1 if ($self->{b} == $inf );
237 7687         9073 my $tmp1 = $self->{a};
238 7687 50       34837 $tmp1 = $tmp1->datetime if UNIVERSAL::can( $tmp1, 'datetime' );
239 7687         9383 $tmp1 = "$tmp1";
240 7687         9207 my $tmp2 = $self->{b};
241 7687 50       28191 $tmp2 = $tmp2->datetime if UNIVERSAL::can( $tmp2, 'datetime' );
242 7687         8292 $tmp2 = "$tmp2";
243 7687 100       28623 return $tmp1 if $tmp1 eq $tmp2;
244 3050 100       8090 $s = $self->{open_begin} ? $set->separators(2) : $set->separators(0);
245 3050         6148 $s .= $tmp1 . $set->separators(4) . $tmp2;
246 3050 100       8277 $s .= $self->{open_end} ? $set->separators(3) : $set->separators(1);
247 3050         10677 return $s;
248             }
249              
250             # end of "_simple_" methods
251              
252              
253             sub type {
254 2553     2553 0 3167 my $self = shift;
255 2553 50       5415 unless (@_) {
256 2553 100       9726 return ref($self) ? $self->{type} : $Type;
257             }
258 0         0 my $tmp_type = shift;
259 0         0 eval "use " . $tmp_type;
260 0 0       0 carp "Warning: can't start $tmp_type : $@" if $@;
261 0 0       0 if (ref($self)) {
262 0         0 $self->{type} = $tmp_type;
263 0         0 return $self;
264             }
265             else {
266 0         0 $Type = $tmp_type;
267 0         0 return $Type;
268             }
269             }
270              
271             sub list {
272 8     8 0 10 my $self = shift;
273 8         128 my @b = ();
274 8         7 foreach (@{$self->{list}}) {
  8         18  
275 52         95 push @b, $self->new($_);
276             }
277 8         69 return @b;
278             }
279              
280             sub fixtype {
281 2009     2009 0 2177 my $self = shift;
282 2009         3507 $self = $self->copy;
283 2009         2866 $self->{fixtype} = 1;
284 2009         4010 my $type = $self->type;
285 2009 50       7121 return $self unless $type;
286 0         0 foreach (@{$self->{list}}) {
  0         0  
287 0 0       0 $_->{a} = $type->new($_->{a}) unless ref($_->{a}) eq $type;
288 0 0       0 $_->{b} = $type->new($_->{b}) unless ref($_->{b}) eq $type;
289             }
290 0         0 return $self;
291             }
292              
293             sub numeric {
294 0     0 0 0 my $self = shift;
295 0 0       0 return $self unless $self->{fixtype};
296 0         0 $self = $self->copy;
297 0         0 $self->{fixtype} = 0;
298 0         0 foreach (@{$self->{list}}) {
  0         0  
299 0         0 $_->{a} = 0 + $_->{a};
300 0         0 $_->{b} = 0 + $_->{b};
301             }
302 0         0 return $self;
303             }
304              
305 0     0   0 sub _no_cleanup { $_[0] } # obsolete
306              
307             sub first {
308 557     557 0 685 my $self = $_[0];
309 557 50       1292 if (exists $self->{first} ) {
310 0 0       0 return wantarray ? @{$self->{first}} : $self->{first}[0];
  0         0  
311             }
312 557 50       601 unless ( @{$self->{list}} ) {
  557         1324  
313 0 0       0 return wantarray ? (undef, 0) : undef;
314             }
315 557         1329 my $first = $self->new( $self->{list}[0] );
316 557 100       1280 return $first unless wantarray;
317 535         969 my $res = $self->new;
318 535         624 push @{$res->{list}}, @{$self->{list}}[1 .. $#{$self->{list}}];
  535         1202  
  535         845  
  535         944  
319 535 100       1575 return @{$self->{first}} = ($first) if $res->is_null;
  495         2720  
320 40         86 return @{$self->{first}} = ($first, $res);
  40         342  
321             }
322              
323             sub last {
324 309     309 0 397 my $self = $_[0];
325 309 50       862 if (exists $self->{last} ) {
326 0 0       0 return wantarray ? @{$self->{last}} : $self->{last}[0];
  0         0  
327             }
328 309 50       321 unless ( @{$self->{list}} ) {
  309         777  
329 0 0       0 return wantarray ? (undef, 0) : undef;
330             }
331 309         806 my $last = $self->new( $self->{list}[-1] );
332 309 100       657 return $last unless wantarray;
333 297         715 my $res = $self->new;
334 297         356 push @{$res->{list}}, @{$self->{list}}[0 .. $#{$self->{list}}-1];
  297         674  
  297         475  
  297         590  
335 297 100       1038 return @{$self->{last}} = ($last) if $res->is_null;
  290         1513  
336 7         10 return @{$self->{last}} = ($last, $res);
  7         238  
337             }
338              
339             sub is_null {
340 1573 100   1573 0 1594 @{$_[0]->{list}} ? 0 : 1;
  1573         6732  
341             }
342              
343             sub is_empty {
344 253     253 0 692 $_[0]->is_null;
345             }
346              
347             sub is_nonempty {
348 0     0 0 0 ! $_[0]->is_null;
349             }
350              
351             sub is_span {
352 0 0   0 0 0 ( $#{$_[0]->{list}} == 0 ) ? 1 : 0;
  0         0  
353             }
354              
355             sub is_singleton {
356 0 0 0 0 0 0 ( $#{$_[0]->{list}} == 0 &&
357             $_[0]->{list}[0]{a} == $_[0]->{list}[0]{b} ) ? 1 : 0;
358             }
359              
360             sub is_subset {
361 0     0 0 0 my $a1 = shift;
362 0         0 my $b1;
363 0 0       0 if (ref ($_[0]) eq ref($a1) ) {
364 0         0 $b1 = shift;
365             }
366             else {
367 0         0 $b1 = $a1->new(@_);
368             }
369 0         0 return $b1->contains( $a1 );
370             }
371              
372             sub is_proper_subset {
373 0     0 0 0 my $a1 = shift;
374 0         0 my $b1;
375 0 0       0 if (ref ($_[0]) eq ref($a1) ) {
376 0         0 $b1 = shift;
377             }
378             else {
379 0         0 $b1 = $a1->new(@_);
380             }
381              
382 0         0 my $contains = $b1->contains( $a1 );
383 0 0       0 return $contains unless $contains;
384            
385 0         0 my $equal = ( $a1 == $b1 );
386 0 0 0     0 return $equal if !defined $equal || $equal;
387              
388 0         0 return 1;
389             }
390              
391             sub is_disjoint {
392 0     0 0 0 my $intersects = shift->intersects( @_ );
393 0 0       0 return ! $intersects if defined $intersects;
394 0         0 return $intersects;
395             }
396              
397             sub iterate {
398             # TODO: options 'no-sort', 'no-merge', 'keep-null' ...
399 1628     1628 0 1841 my $a1 = shift;
400 1628         2693 my $iterate = $a1->empty_set();
401 1628         1841 my (@tmp, $ia);
402 1628         2400 my $subroutine = shift;
403 1628         1736 foreach $ia (0 .. $#{$a1->{list}}) {
  1628         3918  
404 1980         4778 @tmp = $subroutine->( $a1->new($a1->{list}[$ia]), @_ );
405 1980 50       17922 $iterate = $iterate->union(@tmp) if @tmp;
406             }
407 1628         5716 return $iterate;
408             }
409              
410              
411             sub intersection {
412 2570     2570 0 3227 my $a1 = shift;
413 2570 50       7283 my $b1 = ref ($_[0]) eq ref($a1) ? $_[0] : $a1->new(@_);
414 2570         5449 return _intersection ( 'intersection', $a1, $b1 );
415             }
416              
417             sub intersects {
418 3425     3425 0 4089 my $a1 = shift;
419 3425 50       11426 my $b1 = ref ($_[0]) eq ref($a1) ? $_[0] : $a1->new(@_);
420 3425         6573 return _intersection ( 'intersects', $a1, $b1 );
421             }
422              
423             sub intersected_spans {
424 8     8 0 15 my $a1 = shift;
425 8 50       30 my $b1 = ref ($_[0]) eq ref($a1) ? $_[0] : $a1->new(@_);
426 8         19 return _intersection ( 'intersected_spans', $a1, $b1 );
427             }
428              
429              
430             sub _intersection {
431 6003     6003   8596 my ( $op, $a1, $b1 ) = @_;
432              
433 6003         5596 my $ia;
434 6003         5995 my ( $a0, $na ) = ( 0, $#{$a1->{list}} );
  6003         12417  
435 6003         7444 my ( $tmp1, $tmp1a, $tmp2a, $tmp1b, $tmp2b, $i_beg, $i_end, $open_beg, $open_end );
436 0         0 my ( $cmp1, $cmp2 );
437 0         0 my @a;
438              
439             # for-loop optimization (makes little difference)
440             # This was kept for backward compatibility with Date::Set tests
441 6003         6011 my $self = $a1;
442 6003 100       6489 if ($na < $#{ $b1->{list} })
  6003         17663  
443             {
444 1181         1402 $na = $#{ $b1->{list} };
  1181         1953  
445 1181         2076 ($a1, $b1) = ($b1, $a1);
446             }
447             # ---
448              
449 6003         6924 B: foreach my $tmp2 ( @{ $b1->{list} } ) {
  6003         11409  
450 6038         7761 $tmp2a = $tmp2->{a};
451 6038         7077 $tmp2b = $tmp2->{b};
452 6038         9232 A: foreach $ia ($a0 .. $na) {
453 7667         12310 $tmp1 = $a1->{list}[$ia];
454 7667         9461 $tmp1b = $tmp1->{b};
455              
456 7667 100       14403 if ($tmp1b < $tmp2a) {
457 1819         1894 $a0++;
458 1819         5273 next A;
459             }
460 5848         7183 $tmp1a = $tmp1->{a};
461 5848 100       12692 if ($tmp1a > $tmp2b) {
462 354         896 next B;
463             }
464              
465 5494         6648 $cmp1 = $tmp1a <=> $tmp2a;
466 5494 100       11057 if ( $cmp1 < 0 ) {
    100          
467 1666         1913 $tmp1a = $tmp2a;
468 1666         2377 $open_beg = $tmp2->{open_begin};
469             }
470             elsif ( $cmp1 ) {
471 1893         2847 $open_beg = $tmp1->{open_begin};
472             }
473             else {
474 1935   100     6284 $open_beg = $tmp1->{open_begin} || $tmp2->{open_begin};
475             }
476              
477 5494         6626 $cmp2 = $tmp1b <=> $tmp2b;
478 5494 100       10531 if ( $cmp2 > 0 ) {
    100          
479 1316         1621 $tmp1b = $tmp2b;
480 1316         1734 $open_end = $tmp2->{open_end};
481             }
482             elsif ( $cmp2 ) {
483 2495         3370 $open_end = $tmp1->{open_end};
484             }
485             else {
486 1683   100     4912 $open_end = $tmp1->{open_end} || $tmp2->{open_end};
487             }
488              
489 5494 100 100     38911 if ( ( $tmp1a <= $tmp1b ) &&
      33        
490             ( ($tmp1a != $tmp1b) ||
491             (!$open_beg and !$open_end) ||
492             ($tmp1a == $inf) || # XXX
493             ($tmp1a == $neg_inf)
494             )
495             )
496             {
497 4469 100       9145 if ( $op eq 'intersection' )
498             {
499 2519         9718 push @a, {
500             a => $tmp1a, b => $tmp1b,
501             open_begin => $open_beg, open_end => $open_end } ;
502             }
503 4469 100       9554 if ( $op eq 'intersects' )
504             {
505 1926         14550 return 1;
506             }
507 2543 100       8562 if ( $op eq 'intersected_spans' )
508             {
509 24         35 push @a, $tmp1;
510 24         19 $a0++;
511 24         46 next A;
512             }
513             }
514             }
515             }
516              
517 4077 100       20733 return 0 if $op eq 'intersects';
518            
519 2578         5012 my $intersection = $self->new();
520 2578         4458 $intersection->{list} = \@a;
521 2578         12658 return $intersection;
522             }
523              
524              
525             sub complement {
526 1174     1174 0 1561 my $self = shift;
527 1174 50       2621 if (@_) {
528 0         0 my $a1;
529 0 0       0 if (ref ($_[0]) eq ref($self) ) {
530 0         0 $a1 = shift;
531             }
532             else {
533 0         0 $a1 = $self->new(@_);
534             }
535 0         0 return $self->intersection( $a1->complement );
536             }
537              
538 1174 100       1267 unless ( @{$self->{list}} ) {
  1174         3120  
539 1         5 return $self->universal_set;
540             }
541 1173         2390 my $complement = $self->empty_set();
542 1173         3274 @{$complement->{list}} = _simple_complement($self->{list}[0]);
  1173         3157  
543              
544 1173         2536 my $tmp = $self->empty_set();
545 1173         1561 foreach my $ia (1 .. $#{$self->{list}}) {
  1173         3826  
546 79         203 @{$tmp->{list}} = _simple_complement($self->{list}[$ia]);
  79         199  
547 79         265 $complement = $complement->intersection($tmp);
548             }
549 1173         6153 return $complement;
550             }
551              
552              
553             sub until {
554 111     111 1 142 my $a1 = shift;
555 111         127 my $b1;
556 111 50       281 if (ref ($_[0]) eq ref($a1) ) {
557 111         137 $b1 = shift;
558             }
559             else {
560 0         0 $b1 = $a1->new(@_);
561             }
562 111         321 my @b1_min = $b1->min_a;
563 111         384 my @a1_max = $a1->max_a;
564              
565 111 100       548 unless (defined $b1_min[0]) {
566 2         7 return $a1->until($inf);
567             }
568 109 100       204 unless (defined $a1_max[0]) {
569 19         54 return $a1->new(-$inf)->until($b1);
570             }
571              
572 90         110 my ($ia, $ib, $begin, $end);
573 90         114 $ia = 0;
574 90         126 $ib = 0;
575              
576 90         199 my $u = $a1->new;
577 90         151 my $last = -$inf;
578 90   100     129 while ( ($ia <= $#{$a1->{list}}) && ($ib <= $#{$b1->{list}})) {
  241         725  
  159         533  
579 151         334 $begin = $a1->{list}[$ia]{a};
580 151         259 $end = $b1->{list}[$ib]{b};
581 151 100       439 if ( $end <= $begin ) {
582 37         46 push @{$u->{list}}, {
  37         161  
583             a => $last ,
584             b => $end ,
585             open_begin => 0 ,
586             open_end => 1 };
587 37         50 $ib++;
588 37         44 $last = $end;
589 37         42 next;
590             }
591 114         139 push @{$u->{list}}, {
  114         431  
592             a => $begin ,
593             b => $end ,
594             open_begin => 0 ,
595             open_end => 1 };
596 114         141 $ib++;
597 114         104 $ia++;
598 114         144 $last = $end;
599             }
600 90 100 100     118 if ($ia <= $#{$a1->{list}} &&
  90         355  
601             $a1->{list}[$ia]{a} >= $last )
602             {
603 7         11 push @{$u->{list}}, {
  7         38  
604             a => $a1->{list}[$ia]{a} ,
605             b => $inf ,
606             open_begin => 0 ,
607             open_end => 1 };
608             }
609 90         591 return $u;
610             }
611              
612             sub start_set {
613             return $_[0]->iterate(
614 4     4   12 sub { $_[0]->min }
615 1     1 0 9 );
616             }
617              
618              
619             sub end_set {
620             return $_[0]->iterate(
621 4     4   80 sub { $_[0]->max }
622 1     1 0 9 );
623             }
624              
625             sub union {
626 3300     3300 0 3629 my $a1 = shift;
627 3300         3090 my $b1;
628 3300 50       6286 if (ref ($_[0]) eq ref($a1) ) {
629 3300         3761 $b1 = shift;
630             }
631             else {
632 0         0 $b1 = $a1->new(@_);
633             }
634             # test for union with empty set
635 3300 100       2951 if ( $#{ $a1->{list} } < 0 ) {
  3300         8264  
636 1630         7200 return $b1;
637             }
638 1670 100       2032 if ( $#{ $b1->{list} } < 0 ) {
  1670         3959  
639 2         15 return $a1;
640             }
641 1668         4361 my @b1_min = $b1->min_a;
642 1668         4769 my @a1_max = $a1->max_a;
643 1668 50       4025 unless (defined $b1_min[0]) {
644 0         0 return $a1;
645             }
646 1668 50       2930 unless (defined $a1_max[0]) {
647 0         0 return $b1;
648             }
649 1668         1512 my ($ia, $ib);
650 1668         1718 $ia = 0;
651 1668         1511 $ib = 0;
652              
653             # size+order matters on speed
654 1668         2997 $a1 = $a1->new($a1); # don't modify ourselves
655 1668         2438 my $b_list = $b1->{list};
656             # -- frequent case - $b1 is after $a1
657 1668 100       3794 if ($b1_min[0] > $a1_max[0]) {
658 42         55 push @{$a1->{list}}, @$b_list;
  42         73  
659 42         270 return $a1;
660             }
661              
662 1626         1826 my @tmp;
663 1626   33     5849 my $is_real = !$a1->tolerance && !$b1->tolerance;
664 1626         1843 B: foreach $ib ($ib .. $#{$b_list}) {
  1626         3955  
665 1834         1775 foreach $ia ($ia .. $#{$a1->{list}}) {
  1834         3783  
666 2049         5189 @tmp = _simple_union($a1->{list}[$ia], $b_list->[$ib], $a1->{tolerance});
667 2049 100       4693 if ($#tmp == 0) {
668 1623         2576 $a1->{list}[$ia] = $tmp[0];
669              
670 1623         1682 while (1) {
671 1627 100       1480 last if $ia >= $#{$a1->{list}};
  1627         4183  
672 215 100 66     852 last unless _simple_intersects ( $a1->{list}[$ia], $a1->{list}[$ia + 1] )
      66        
673             || $is_real
674             && $a1->{list}[$ia]{b} == $a1->{list}[$ia + 1]{a};
675 4         15 @tmp = _simple_union($a1->{list}[$ia], $a1->{list}[$ia + 1], $a1->{tolerance});
676 4 50       12 last unless @tmp == 1;
677 4         8 $a1->{list}[$ia] = $tmp[0];
678 4         9 splice( @{$a1->{list}}, $ia + 1, 1 );
  4         13  
679             }
680            
681 1623         4650 next B;
682             }
683 426 100       1420 if ($a1->{list}[$ia]{a} >= $b_list->[$ib]{a}) {
684 207         278 splice (@{$a1->{list}}, $ia, 0, $b_list->[$ib]);
  207         585  
685 207         648 next B;
686             }
687             }
688 4         9 push @{$a1->{list}}, $b_list->[$ib];
  4         13  
689             }
690 1626         11431 return $a1;
691             }
692              
693              
694             # there are some ways to process 'contains':
695             # A CONTAINS B IF A == ( A UNION B )
696             # - faster
697             # A CONTAINS B IF B == ( A INTERSECTION B )
698             # - can backtrack = works for unbounded sets
699             sub contains {
700 0     0 0 0 my $a1 = shift;
701 0         0 my $b1 = $a1->union(@_);
702 0 0       0 return ($b1 == $a1) ? 1 : 0;
703             }
704              
705              
706             sub copy {
707 3383     3383 1 4109 my $self = shift;
708 3383         5323 my $copy = $self->empty_set();
709             ## return $copy unless ref($self); # constructor!
710 3383         3871 foreach my $key (keys %{$self}) {
  3383         13897  
711 20094 100       36706 if ( ref( $self->{$key} ) eq 'ARRAY' ) {
712 5865         5669 @{ $copy->{$key} } = @{ $self->{$key} };
  5865         15722  
  5865         8049  
713             }
714             else {
715 14229         25666 $copy->{$key} = $self->{$key};
716             }
717             }
718 3383         10117 return $copy;
719             }
720              
721             *clone = \©
722              
723              
724             sub new {
725 32514     32514 0 197046 my $class = shift;
726 32514         32228 my $self;
727 32514 100       59781 if ( ref $class ) {
728 31970         151322 $self = bless {
729             list => [],
730             tolerance => $class->{tolerance},
731             type => $class->{type},
732             fixtype => $class->{fixtype},
733             }, ref($class);
734             }
735             else {
736 544 50       2202 $self = bless {
    50          
737             list => [],
738             tolerance => $tolerance ? $tolerance : 0,
739             type => $class->type,
740             fixtype => $fixtype ? $fixtype : 0,
741             }, $class;
742             }
743 32514         38473 my ($tmp, $tmp2, $ref);
744 32514         82878 while (@_) {
745 12621         14153 $tmp = shift;
746 12621         16810 $ref = ref($tmp);
747 12621 100       21491 if ($ref) {
748 7911 100       15709 if ($ref eq 'ARRAY') {
749             # allows arrays of arrays
750 96         265 $tmp = $class->new(@$tmp); # call new() recursively
751 96         129 push @{ $self->{list} }, @{$tmp->{list}};
  96         173  
  96         183  
752 96         236 next;
753             }
754 7815 100       13671 if ($ref eq 'HASH') {
755 6103         5558 push @{ $self->{list} }, $tmp;
  6103         12638  
756 6103         15513 next;
757             }
758 1712 50       5795 if ($tmp->isa(__PACKAGE__)) {
759 1712         1730 push @{ $self->{list} }, @{$tmp->{list}};
  1712         2578  
  1712         3086  
760 1712         4399 next;
761             }
762             }
763 4710 100       7735 if ( @_ ) {
764 2053         2547 $tmp2 = shift
765             }
766             else {
767 2657         3117 $tmp2 = $tmp
768             }
769 4710         6523 push @{ $self->{list} }, _simple_new($tmp,$tmp2, $self->{type} )
  4710         17776  
770             }
771 32514         77957 $self;
772             }
773              
774             sub empty_set {
775 16408     16408 1 33898 $_[0]->new;
776             }
777              
778             sub universal_set {
779 2     2 1 14 $_[0]->new( NEG_INFINITY, INFINITY );
780             }
781              
782             *minus = \∁
783              
784             *difference = \∁
785              
786             sub symmetric_difference {
787 1     1 0 54 my $a1 = shift;
788 1         2 my $b1;
789 1 50       4 if (ref ($_[0]) eq ref($a1) ) {
790 0         0 $b1 = shift;
791             }
792             else {
793 1         4 $b1 = $a1->new(@_);
794             }
795              
796 1         6 return $a1->complement( $b1 )->union(
797             $b1->complement( $a1 ) );
798             }
799              
800             *simmetric_difference = \&symmetric_difference; # bugfix
801              
802             sub min {
803 597     597 0 2060 ($_[0]->min_a)[0];
804             }
805              
806             sub min_a {
807 7299     7299 0 7620 my $self = $_[0];
808 7299 50       14828 return @{$self->{min}} if exists $self->{min};
  0         0  
809 7299 100       7285 return @{$self->{min}} = (undef, 0) unless @{$self->{list}};
  72         319  
  7299         16865  
810 7227         13946 my $tmp = $self->{list}[0]{a};
811 7227   100     25622 my $tmp2 = $self->{list}[0]{open_begin} || 0;
812 7227 100 100     15996 if ($tmp2 && $self->{tolerance}) {
813 5         8 $tmp2 = 0;
814 5         9 $tmp += $self->{tolerance};
815             }
816 7227         8146 return @{$self->{min}} = ($tmp, $tmp2);
  7227         38346  
817             };
818              
819             sub max {
820 360     360 0 1539 ($_[0]->max_a)[0];
821             }
822              
823             sub max_a {
824 6659     6659 0 13799 my $self = $_[0];
825 6659 50       14204 return @{$self->{max}} if exists $self->{max};
  0         0  
826 6659 100       6348 return @{$self->{max}} = (undef, 0) unless @{$self->{list}};
  88         373  
  6659         17466  
827 6571         14721 my $tmp = $self->{list}[-1]{b};
828 6571   100     20575 my $tmp2 = $self->{list}[-1]{open_end} || 0;
829 6571 100 100     17528 if ($tmp2 && $self->{tolerance}) {
830 8         11 $tmp2 = 0;
831 8         12 $tmp -= $self->{tolerance};
832             }
833 6571         6696 return @{$self->{max}} = ($tmp, $tmp2);
  6571         28580  
834             };
835              
836             sub count {
837 2     2 0 3 1 + $#{$_[0]->{list}};
  2         18  
838             }
839              
840             sub size {
841 9     9 0 13 my $self = $_[0];
842 9         9 my $size;
843 9         10 foreach( @{$self->{list}} ) {
  9         21  
844 12 100       24 if ( $size ) {
845 3         8 $size += $_->{b} - $_->{a};
846             }
847             else {
848 9         17 $size = $_->{b} - $_->{a};
849             }
850 12 100       36 if ( $self->{tolerance} ) {
851 5 100       13 $size += $self->{tolerance} unless $_->{open_end};
852 5 50       22 $size -= $self->{tolerance} if $_->{open_begin};
853 5 100       18 $size -= $self->{tolerance} if $_->{open_end};
854             }
855             }
856 9         57 return $size;
857             };
858              
859             sub span {
860 1016     1016 0 1674 my $self = $_[0];
861 1016         2548 my @max = $self->max_a;
862 1016         2945 my @min = $self->min_a;
863 1016 50 33     4677 return undef unless defined $min[0] && defined $max[0];
864 1016         2233 my $a1 = $self->new($min[0], $max[0]);
865 1016         2415 $a1->{list}[0]{open_end} = $max[1];
866 1016         1543 $a1->{list}[0]{open_begin} = $min[1];
867 1016         2627 return $a1;
868             };
869              
870             sub spaceship {
871 72     72 0 125 my ($tmp1, $tmp2, $inverted) = @_;
872 72 50       189 if ($inverted) {
873 0         0 ($tmp2, $tmp1) = ($tmp1, $tmp2);
874             }
875 72         103 foreach(0 .. $#{$tmp1->{list}}) {
  72         228  
876 75         156 my $this = $tmp1->{list}[$_];
877 75 50       187 if ($_ > $#{ $tmp2->{list} } ) {
  75         205  
878 0         0 return 1;
879             }
880 75         128 my $other = $tmp2->{list}[$_];
881 75         160 my $cmp = _simple_spaceship($this, $other);
882 75 100       423 return $cmp if $cmp; # this != $other;
883             }
884 23 100       37 return $#{ $tmp1->{list} } == $#{ $tmp2->{list} } ? 0 : -1;
  23         40  
  23         199  
885             }
886              
887             sub tolerance {
888 13     13 0 39 my $self = shift;
889 13         16 my $tmp = pop;
890 13 50       33 if (ref($self)) {
891             # local
892 13 50       28 return $self->{tolerance} unless defined $tmp;
893 13         40 $self = $self->copy;
894 13         24 $self->{tolerance} = $tmp;
895 13         20 delete $self->{max}; # tolerance may change "max"
896              
897 13         17 $_ = 1;
898 13         18 my @tmp;
899 13         16 while ( $_ <= $#{$self->{list}} ) {
  17         58  
900 4         24 @tmp = Set::Infinite::Basic::_simple_union($self->{list}->[$_],
901             $self->{list}->[$_ - 1],
902             $self->{tolerance});
903 4 100       11 if ($#tmp == 0) {
904 1         3 $self->{list}->[$_ - 1] = $tmp[0];
905 1         2 splice (@{$self->{list}}, $_, 1);
  1         2  
906             }
907             else {
908 3         6 $_ ++;
909             }
910             }
911              
912 13         43 return $self;
913             }
914             # global
915 0 0       0 $tolerance = $tmp if defined($tmp);
916 0         0 return $tolerance;
917             }
918              
919             sub integer {
920 8     8 0 232 $_[0]->tolerance (1);
921             }
922              
923             sub real {
924 4     4 0 114 $_[0]->tolerance (0);
925             }
926              
927             sub as_string {
928 6974     6974 0 8016 my $self = shift;
929 7687         14405 return $self->separators(6) .
930             join( $self->separators(5),
931 6974         13205 map { $self->_simple_as_string($_) } @{$self->{list}} ) .
  6974         15693  
932             $self->separators(7),;
933             }
934              
935              
936 0     0     sub DESTROY {}
937              
938             1;
939              
940             __END__