File Coverage

blib/lib/Set/IntSpan.pm
Criterion Covered Total %
statement 760 771 98.5
branch 347 364 95.3
condition 169 179 94.4
subroutine 86 88 97.7
pod 51 61 83.6
total 1413 1463 96.5


line stmt bran cond sub pod time code
1             package Set::IntSpan;
2              
3 20     20   18761 use 5;
  20         82  
  20         1565  
4 20     20   23293 use if $Set::IntSpan::integer, qw(integer);
  20         188  
  20         98  
5 20     20   1772 use strict;
  20         40  
  20         716  
6 20     20   97 use base qw(Exporter);
  20         30  
  20         2500  
7 20     20   113 use Carp;
  20         33  
  20         4067  
8              
9             our $VERSION = '1.19';
10             our @EXPORT_OK = qw(grep_set map_set grep_spans map_spans);
11              
12             use overload
13             '+' => 'union' ,
14             '-' => 'diff' ,
15             '*' => 'intersect' ,
16             '^' => 'xor' ,
17             '~' => 'complement',
18              
19             '+=' => 'U' ,
20             '-=' => 'D' ,
21             '*=' => 'I' ,
22             '^=' => 'X' ,
23              
24             'eq' => 'set_eq' ,
25             'ne' => 'set_ne' ,
26             'lt' => 'set_lt' ,
27             'le' => 'set_le' ,
28             'gt' => 'set_gt' ,
29             'ge' => 'set_ge' ,
30              
31             '<=>' => 'spaceship' ,
32             'cmp' => 'spaceship' ,
33              
34             '""' => 'run_list' ,
35 20     20   51337 'bool' => sub { not shift->empty };
  20     3   26379  
  20         217  
  3         21  
36              
37             sub _reorder # restore the order of args that are reversed by operator overloads
38             {
39 278 100   278   569 if ($_[2])
40             {
41 5         6 my $temp = $_[0];
42 5         6 $_[0] = $_[1];
43 5         8 $_[1] = $temp;
44             }
45             }
46              
47             sub set_eq
48             {
49 52     52 0 241 my($a, $set_spec) = @_;
50 52         104 my $b = $a->_real_set($set_spec);
51 52         106 $a->equal($b)
52             }
53              
54             sub set_le
55             {
56 7     7 0 37 my($a, $set_spec, $reverse) = @_;
57 7         11 my $b = $a->_real_set($set_spec);
58 7         12 _reorder($a, $b, $reverse);
59 7         13 $a->subset($b)
60             }
61              
62             sub set_ge
63             {
64 7     7 0 33 my($a, $set_spec, $reverse) = @_;
65 7         14 my $b = $a->_real_set($set_spec);
66 7         14 _reorder($a, $b, $reverse);
67 7         18 $a->superset($b)
68             }
69              
70 4     4 0 28 sub set_ne { not &set_eq }
71 3 100   3 0 25 sub set_lt { &set_le and not &set_eq }
72 3 100   3 0 26 sub set_gt { &set_ge and not &set_eq }
73              
74              
75             sub set_cmp
76             {
77 0     0 0 0 my($a, $b, $reverse) = @_;
78 0         0 $b = $a->_real_set($b);
79              
80 0         0 _reorder($a, $b, $reverse);
81              
82 0 0       0 $a->equal($b) ? 0 : 1;
83             }
84              
85              
86             sub spaceship
87             {
88 21     21 0 130 my($a, $b, $reverse) = @_;
89 21 50       51 ref $a and $a = $a->size;
90 21 100       33 ref $b and $b = $b->size;
91              
92 21         32 _reorder($a, $b, $reverse);
93              
94 21 100       36 $a == $b and return 0;
95 14 100       23 $a < 0 and return 1;
96 12 100       18 $b < 0 and return -1;
97 11         23 $a <=> $b
98             }
99              
100              
101             $Set::IntSpan::Empty_String = '-';
102              
103              
104             sub new
105             {
106 3427     3427 1 41877 my($this, $set_spec, @set_specs) = @_;
107              
108 3427   66     11343 my $class = ref($this) || $this;
109 3427         7622 my $set = bless { }, $class;
110 3427         9131 $set->{empty_string} = \$Set::IntSpan::Empty_String;
111 3427         8194 $set->copy($set_spec);
112              
113 3416         7426 while (@set_specs)
114             {
115 13         45 $set = $set->union(shift @set_specs);
116             }
117              
118             $set
119 3416         12991 }
120              
121              
122             sub valid
123             {
124 11     11 1 243 my($this, $run_list) = @_;
125 11   33     42 my $class = ref($this) || $this;
126 11         20 my $set = new $class;
127              
128 11         19 eval { $set->_copy_run_list($run_list) };
  11         24  
129 11 50       61 $@ ? 0 : 1
130             }
131              
132              
133             sub copy
134             {
135 3427     3427 1 4498 my($set, $set_spec) = @_;
136              
137 3427 100       7400 SWITCH:
138             {
139 3427         12347 defined $set_spec or $set->_copy_empty ( ), last;
140 2498 100       10125 ref $set_spec or $set->_copy_run_list($set_spec), last;
141 98 100       304 ref $set_spec eq 'ARRAY' and $set->_copy_array ($set_spec), last;
142 50         118 $set->_copy_set ($set_spec) ;
143             }
144              
145             $set
146 3416         4690 }
147              
148              
149             sub _copy_empty # makes $set the empty set
150             {
151 3340     3340   4072 my $set = shift;
152              
153 3340         5677 $set->{negInf} = 0;
154 3340         4595 $set->{posInf} = 0;
155 3340         7175 $set->{edges } = [];
156             }
157              
158              
159             sub _copy_array # copies an array into a set
160             {
161 48     48   67 my($set, $array) = @_;
162              
163 48         87 my @spans = grep { ref } @$array;
  110         227  
164 48         75 my @elements = sort { $a <=> $b } grep { not ref } @$array;
  86         129  
  110         238  
165              
166 48         1617 my @span;
167 48         79 for my $e (@elements)
168             {
169 74 100 100     524 if (@span==0)
    100 100        
    100 100        
    100 100        
    100          
170             {
171 23         44 push @span, $e;
172             }
173             elsif (@span==1 and $e==$span[0]+1)
174             {
175 19         34 push @span, $e;
176             }
177             elsif (@span==1 and $e >$span[0]+1)
178             {
179 6         15 push @spans, [ $span[0], $span[0] ];
180 6         14 @span = ($e);
181             }
182             elsif (@span==2 and $e==$span[1]+1)
183             {
184 11         27 $span[1] = $e;
185             }
186             elsif (@span==2 and $e >$span[1]+1)
187             {
188 6         15 push @spans, [ @span ];
189 6         15 @span = ($e);
190             }
191             }
192              
193 48 100       117 @span==1 and push @spans, [ $span[0], $span[0] ];
194 48 100       103 @span==2 and push @spans, [ @span ];
195              
196 48         123 $set->_insert_spans(\@spans)
197             }
198              
199             sub bySpan
200             {
201 95     95 0 125 my($al, $au) = @$a;
202 95         121 my($bl, $bu) = @$b;
203              
204 95 100 100     371 if (defined $al && defined $bl) { return $al <=> $bl; }
  77 100       158  
    100          
    50          
    50          
205 5         9 elsif (defined $al ) { return 1; }
206 12         20 elsif ( defined $bl) { return -1; }
207 0         0 elsif (defined $au ) { return -1; }
208 0         0 elsif ( defined $bu) { return 1; }
209 1         4 else { return 0; }
210             }
211              
212             sub _insert_spans
213             {
214 108     108   128 my($set, $spans) = @_;
215              
216 108         111 my @edges;
217 108         152 $set->{negInf} = 0;
218 108         147 $set->{posInf} = 0;
219 108         157 $set->{edges } = \@edges;
220              
221 108         331 my @spans = sort bySpan @$spans;
222              
223 108 100 100     1001 if (@spans and not defined $spans[0][0])
224             {
225 24         40 $set->{negInf} = 1;
226 24         28 my $span = shift @spans;
227              
228 24 100       59 if (not defined $span->[1])
229             {
230 8         53 $set->{posInf} = 1;
231 8         30 return $set;
232             }
233              
234 16         94 push @edges, $span->[1];
235              
236 16   66     81 while (@spans and not defined $spans[0][0])
237             {
238 0         0 my $span = shift @spans;
239 0 0       0 $edges[0] = $span->[1] if $edges[0] < $span->[1];
240             }
241             }
242              
243 100         172 for (@spans) { $_->[0]--; }
  130         225  
244              
245 100 100 100     386 if (@spans and not @edges)
246             {
247 65         199 my $span = shift @spans;
248              
249 65 100       128 if (defined $span->[1])
250             {
251 59         195 push @edges, @$span;
252             }
253             else
254             {
255 6         14 push @edges, $span->[0];
256 6         10 $set->{posInf} = 1;
257 6         23 return $set;
258             }
259             }
260              
261 94   100     316 while (@spans and defined $spans[0][1])
262             {
263 55         69 my $span = shift @spans;
264 55 100       100 if ($edges[-1] < $span->[0])
265             {
266 47         175 push @edges, @$span;
267             }
268             else
269             {
270 8 100       55 $edges[-1] = $span->[1] if $edges[-1] < $span->[1];
271             }
272             }
273              
274 94 100       183 if (@spans)
275             {
276 10         24 $set->{posInf} = 1;
277 10         126 my $span = shift @spans;
278              
279 10 100       23 if ($edges[-1] < $span->[0])
280             {
281 6         11 push @edges, $span->[0];
282             }
283             else
284             {
285 4         7 pop @edges;
286             }
287             }
288              
289 94         352 return $set
290             }
291              
292              
293             sub _copy_set # copies one set to another
294             {
295 50     50   58 my($dest, $src) = @_;
296              
297 50         87 $dest->{negInf} = $src->{negInf};
298 50         73 $dest->{posInf} = $src->{posInf};
299 50         74 $dest->{edges } = [ @{$src->{edges }} ];
  50         159  
300             }
301              
302              
303             sub _copy_run_list # parses a run list
304             {
305 2411     2411   5657 my($set, $runList) = @_;
306              
307 2411         4180 $set->_copy_empty;
308              
309 2411         8143 $runList =~ s/\s|_//g;
310 2411 100       5195 return if $runList eq '-'; # empty set
311              
312 2160         3111 my($first, $last) = (1, 0); # verifies order of infinite runs
313              
314 2160         2350 my @edges;
315 2160         5525 for my $run (split(/,/ , $runList))
316             {
317 3081 100       6161 croak "Set::IntSpan::_copy_run_list: Bad order 1: $runList\n" if $last;
318              
319             RUN:
320             {
321 3077         2942 $run =~ /^ (-?\d+) $/x and do
322 3077 100       10251 {
323 652         1923 push(@edges, $1-1, $1);
324 652         1146 last RUN;
325             };
326              
327             $run =~ /^ (-?\d+) - (-?\d+) $/x and do
328 2425 100       8244 {
329 1587 100       5157 croak "Set::IntSpan::_copy_run_list: Bad order 2: $runList\n"
330             if $1 > $2;
331 1585         3770 push(@edges, $1-1, $2);
332 1585         2249 last RUN;
333             };
334              
335             $run =~ /^ \( - (-?\d+) $/x and do
336 838 100       2565 {
337 293 100       1016 croak "Set::IntSpan::_copy_run_list: Bad order 3: $runList\n"
338             unless $first;
339 291         435 $set->{negInf} = 1;
340 291         887 push @edges, $1;
341 291         427 last RUN;
342             };
343              
344             $run =~ /^ (-?\d+) - \) $/x and do
345 545 100       2086 {
346 298         704 push @edges, $1-1;
347 298         449 $set->{posInf} = 1;
348 298         914 $last = 1;
349 298         433 last RUN;
350             };
351              
352             $run =~ /^ \( - \) $/x and do
353 247 100       760 {
354 237 50       442 croak "Set::IntSpan::_copy_run_list: Bad order 4: $runList\n"
355             unless $first;
356 237         274 $last = 1;
357 237         354 $set->{negInf} = 1;
358 237         311 $set->{posInf} = 1;
359 237         394 last RUN;
360             };
361              
362 10         1587 croak "Set::IntSpan::_copy_run_list: Bad syntax: $runList\n";
363             }
364              
365 3063         6505 $first = 0;
366             }
367              
368 2142         6500 $set->{edges} = [ @edges ];
369              
370 2142 100       4649 $set->_cleanup or
371             croak "Set::IntSpan::_copy_run_list: Bad order 5: $runList\n";
372             }
373              
374              
375             # check for overlapping runs
376             # delete duplicate edges
377             sub _cleanup
378             {
379 2142     2142   2534 my $set = shift;
380 2142         2961 my $edges = $set->{edges};
381              
382 2142         2313 my $i=0;
383 2142         4831 while ($i < $#$edges)
384             {
385 3116         5259 my $cmp = $$edges[$i] <=> $$edges[$i+1];
386             {
387 3116 100       3491 $cmp == -1 and $i++ , last;
  3116         9219  
388 46 100       781 $cmp == 0 and splice(@$edges, $i, 2), last;
389 4 50       652 $cmp == 1 and return 0;
390             }
391             }
392              
393             1
394 2138         7740 }
395              
396              
397             sub run_list
398             {
399 1058     1058 1 5875 my $set = shift;
400              
401 1058 100       2084 $set->empty and return ${$set->{empty_string}};
  240         848  
402              
403 818         1283 my @edges = @{$set->{edges}};
  818         2093  
404 818         1071 my @runs;
405              
406 818 100       1887 $set->{negInf} and unshift @edges, '(';
407 818 100       2344 $set->{posInf} and push @edges, ')';
408              
409 818         1898 while(@edges)
410             {
411 1239         2273 my($lower, $upper) = splice @edges, 0, 2;
412              
413 1239 100 100     7276 if ($lower ne '(' and $upper ne ')' and $lower+1==$upper)
      100        
414             {
415 305         1087 push @runs, $upper;
416             }
417             else
418             {
419 934 100       1960 $lower ne '(' and $lower++;
420 934         3028 push @runs, "$lower-$upper";
421             }
422             }
423              
424 818         3608 join(',', @runs)
425             }
426              
427             sub dump
428             {
429 0     0 0 0 my $set = shift;
430 0 0       0 ($set->{negInf} ? '(' : '') . join ',', @{$set->{edges}} . ($set->{posInf} ? ')' : '')
  0 0       0  
431             }
432              
433             sub elements
434             {
435 58     58 1 874 my $set = shift;
436              
437 58 100 100     2158 ($set->{negInf} or $set->{posInf}) and
438             croak "Set::IntSpan::elements: infinite set\n";
439              
440 42         45 my @elements;
441 42         41 my @edges = @{$set->{edges}};
  42         121  
442 42         91 while (@edges)
443             {
444 40         81 my($lower, $upper) = splice(@edges, 0, 2);
445 40         304 push @elements, $lower+1 .. $upper;
446             }
447              
448 42 100       187 wantarray ? @elements : \@elements
449             }
450              
451             sub sets
452             {
453 23     23 1 71 my $set = shift;
454 23         28 my @edges = @{$set->{edges}};
  23         62  
455              
456 23 100       53 unshift @edges, undef if $set->{negInf};
457 23 100       50 push @edges, undef if $set->{posInf};
458              
459 23         21 my @sets;
460 23         42 while (@edges)
461             {
462 23         44 my($lower, $upper) = splice(@edges, 0, 2);
463              
464 23 100       59 $lower = defined $lower ? $lower+1 : '(';
465 23 100       41 $upper = defined $upper ? $upper : ')';
466              
467 23         82 push @sets, Set::IntSpan->new("$lower-$upper");
468             }
469              
470             @sets
471 23         68 }
472              
473              
474             sub spans
475             {
476 38     38 1 328 my $set = shift;
477 38         42 my @edges = @{$set->{edges}};
  38         158  
478              
479 38 100       107 unshift @edges, undef if $set->{negInf};
480 38 100       82 push @edges, undef if $set->{posInf};
481              
482 38         37 my @spans;
483 38         76 while (@edges)
484             {
485 52         85 my($lower, $upper) = splice(@edges, 0, 2);
486 52 100       109 $lower++
487             if defined $lower;
488 52         180 push @spans, [$lower, $upper];
489             }
490              
491             @spans
492 38         106 }
493              
494              
495             sub _real_set # converts a set specification into a set
496             {
497 1046     1046   2198 my($set, $set_spec) = @_;
498              
499 1046 100 100     6926 (defined $set_spec and ref $set_spec and ref $set_spec ne 'ARRAY') ?
500             $set_spec :
501             $set->new($set_spec)
502             }
503              
504             sub U
505             {
506 31     31 1 100 my($a, $set_spec) = @_;
507 31         58 my $s = $a->union($set_spec);
508 31         59 $a->{negInf} = $s->{negInf};
509 31         41 $a->{posInf} = $s->{posInf};
510 31         195 $a->{edges } = $s->{edges };
511 31         102 $a
512             }
513              
514             sub union
515             {
516 85     85 1 271 my($a, $set_spec) = @_;
517 85         170 my $b = $a->_real_set($set_spec);
518 85         169 my $s = $a->new;
519              
520 85   100     351 $s->{negInf} = $a->{negInf} || $b->{negInf};
521              
522 85         131 my $eA = $a->{edges};
523 85         106 my $eB = $b->{edges};
524 85         117 my $eS = $s->{edges};
525              
526 85         103 my $inA = $a->{negInf};
527 85         104 my $inB = $b->{negInf};
528              
529 85         90 my $iA = 0;
530 85         113 my $iB = 0;
531              
532 85   100     352 while ($iA<@$eA and $iB<@$eB)
533             {
534 155         194 my $xA = $$eA[$iA];
535 155         168 my $xB = $$eB[$iB];
536              
537 155 100       308 if ($xA < $xB)
    100          
538             {
539 62         61 $iA++;
540 62         73 $inA = ! $inA;
541 62 100       261 not $inB and push(@$eS, $xA);
542             }
543             elsif ($xB < $xA)
544             {
545 58         60 $iB++;
546 58         666 $inB = ! $inB;
547 58 100       289 not $inA and push(@$eS, $xB);
548             }
549             else
550             {
551 35         35 $iA++;
552 35         45 $iB++;
553 35         41 $inA = ! $inA;
554 35         40 $inB = ! $inB;
555 35 100       153 $inA == $inB and push(@$eS, $xA);
556             }
557             }
558              
559 85 100 100     524 $iA < @$eA and ! $inB and push(@$eS, @$eA[$iA..$#$eA]);
560 85 100 100     342 $iB < @$eB and ! $inA and push(@$eS, @$eB[$iB..$#$eB]);
561              
562 85   100     338 $s->{posInf} = $a->{posInf} || $b->{posInf};
563 85         454 $s
564             }
565              
566              
567             sub I
568             {
569 31     31 1 94 my($a, $set_spec) = @_;
570 31         55 my $s = $a->intersect($set_spec);
571 31         48 $a->{negInf} = $s->{negInf};
572 31         36 $a->{posInf} = $s->{posInf};
573 31         44 $a->{edges } = $s->{edges };
574 31         86 $a
575             }
576              
577             sub intersect
578             {
579 67     67 1 209 my($a, $set_spec) = @_;
580 67         128 my $b = $a->_real_set($set_spec);
581 67         120 my $s = $a->new;
582              
583 67   100     180 $s->{negInf} = $a->{negInf} && $b->{negInf};
584              
585 67         93 my $eA = $a->{edges};
586 67         82 my $eB = $b->{edges};
587 67         89 my $eS = $s->{edges};
588              
589 67         80 my $inA = $a->{negInf};
590 67         75 my $inB = $b->{negInf};
591              
592 67         68 my $iA = 0;
593 67         71 my $iB = 0;
594              
595 67   100     244 while ($iA<@$eA and $iB<@$eB)
596             {
597 124         138 my $xA = $$eA[$iA];
598 124         137 my $xB = $$eB[$iB];
599              
600 124 100       207 if ($xA < $xB)
    100          
601             {
602 46         49 $iA++;
603 46         54 $inA = ! $inA;
604 46 100       185 $inB and push(@$eS, $xA);
605             }
606             elsif ($xB < $xA)
607             {
608 43         42 $iB++;
609 43         47 $inB = ! $inB;
610 43 100       267 $inA and push(@$eS, $xB);
611             }
612             else
613             {
614 35         34 $iA++;
615 35         34 $iB++;
616 35         38 $inA = ! $inA;
617 35         38 $inB = ! $inB;
618 35 100       134 $inA == $inB and push(@$eS, $xA);
619             }
620             }
621              
622 67 100 100     169 $iA < @$eA and $inB and push(@$eS, @$eA[$iA..$#$eA]);
623 67 100 100     177 $iB < @$eB and $inA and push(@$eS, @$eB[$iB..$#$eB]);
624              
625 67   100     171 $s->{posInf} = $a->{posInf} && $b->{posInf};
626 67         145 $s
627             }
628              
629              
630             sub D
631             {
632 31     31 1 112 my($a, $set_spec) = @_;
633 31         60 my $s = $a->diff($set_spec);
634 31         49 $a->{negInf} = $s->{negInf};
635 31         76 $a->{posInf} = $s->{posInf};
636 31         49 $a->{edges } = $s->{edges };
637 31         98 $a
638             }
639              
640             sub diff
641             {
642 243     243 1 556 my($a, $set_spec, $reverse) = @_;
643 243         455 my $b = $a->_real_set($set_spec);
644              
645 243         510 _reorder($a, $b, $reverse);
646              
647 243         442 my $s = $a->new;
648              
649 243   100     798 $s->{negInf} = $a->{negInf} && ! $b->{negInf};
650              
651 243         340 my $eA = $a->{edges};
652 243         341 my $eB = $b->{edges};
653 243         309 my $eS = $s->{edges};
654              
655 243         323 my $inA = $a->{negInf};
656 243         339 my $inB = $b->{negInf};
657              
658 243         271 my $iA = 0;
659 243         268 my $iB = 0;
660              
661 243   100     1184 while ($iA<@$eA and $iB<@$eB)
662             {
663 325         469 my $xA = $$eA[$iA];
664 325         398 my $xB = $$eB[$iB];
665              
666 325 100       654 if ($xA < $xB)
    100          
667             {
668 107         112 $iA++;
669 107         142 $inA = ! $inA;
670 107 100       494 not $inB and push(@$eS, $xA);
671             }
672             elsif ($xB < $xA)
673             {
674 107         131 $iB++;
675 107         132 $inB = ! $inB;
676 107 100       591 $inA and push(@$eS, $xB);
677             }
678             else
679             {
680 111         122 $iA++;
681 111         119 $iB++;
682 111         138 $inA = ! $inA;
683 111         132 $inB = ! $inB;
684 111 100       511 $inA != $inB and push(@$eS, $xA);
685             }
686             }
687              
688 243 100 100     851 $iA < @$eA and not $inB and push(@$eS, @$eA[$iA..$#$eA]);
689 243 100 100     784 $iB < @$eB and $inA and push(@$eS, @$eB[$iB..$#$eB]);
690              
691 243   100     690 $s->{posInf} = $a->{posInf} && ! $b->{posInf};
692 243         789 $s
693             }
694              
695              
696             sub X
697             {
698 31     31 1 103 my($a, $set_spec) = @_;
699 31         56 my $s = $a->xor($set_spec);
700 31         82 $a->{negInf} = $s->{negInf};
701 31         40 $a->{posInf} = $s->{posInf};
702 31         50 $a->{edges } = $s->{edges };
703 31         165 $a
704             }
705              
706             sub xor
707             {
708 67     67 1 188 my($a, $set_spec) = @_;
709 67         123 my $b = $a->_real_set($set_spec);
710 67         124 my $s = $a->new;
711              
712 67         131 $s->{negInf} = $a->{negInf} ^ $b->{negInf};
713              
714 67         82 my $eA = $a->{edges};
715 67         83 my $eB = $b->{edges};
716 67         82 my $eS = $s->{edges};
717              
718 67         71 my $iA = 0;
719 67         75 my $iB = 0;
720              
721 67   100     264 while ($iA<@$eA and $iB<@$eB)
722             {
723 122         159 my $xA = $$eA[$iA];
724 122         155 my $xB = $$eB[$iB];
725              
726 122 100       4352 if ($xA < $xB)
    100          
727             {
728 45         51 $iA++;
729 45         529 push(@$eS, $xA);
730             }
731             elsif ($xB < $xA)
732             {
733 43         45 $iB++;
734 43         255 push(@$eS, $xB);
735             }
736             else
737             {
738 34         39 $iA++;
739 34         117 $iB++;
740             }
741             }
742              
743 67 100       165 $iA < @$eA and push(@$eS, @$eA[$iA..$#$eA]);
744 67 100       223 $iB < @$eB and push(@$eS, @$eB[$iB..$#$eB]);
745              
746 67         140 $s->{posInf} = $a->{posInf} ^ $b->{posInf};
747 67         140 $s
748             }
749              
750              
751             sub complement
752             {
753 13     13 1 64 my $set = shift;
754 13         30 $set->new($set)->C
755             }
756              
757             sub C
758             {
759 23     23 1 48 my $set = shift;
760 23         45 $set->{negInf} = ! $set->{negInf};
761 23         38 $set->{posInf} = ! $set->{posInf};
762 23         42 $set
763             }
764              
765              
766             sub superset
767             {
768 88     88 1 329 my($a, $set_spec) = @_;
769 88         171 my $b = $a->_real_set($set_spec);
770              
771 88         198 $b->diff($a)->empty
772             }
773              
774              
775             sub subset
776             {
777 88     88 1 346 my($a, $b) = @_;
778              
779 88         210 $a->diff($b)->empty
780             }
781              
782              
783             sub equal
784             {
785 349     349 1 6900 my($a, $set_spec) = @_;
786 349         1157 my $b = $a->_real_set($set_spec);
787              
788 349 100       1095 $a->{negInf} == $b->{negInf} or return 0;
789 319 100       672 $a->{posInf} == $b->{posInf} or return 0;
790              
791 305         795 my $aEdge = $a->{edges};
792 305         948 my $bEdge = $b->{edges};
793 305 100       610 @$aEdge == @$bEdge or return 0;
794              
795 287         642 for (my $i=0; $i<@$aEdge; $i++)
796             {
797 564 100       1670 $$aEdge[$i] == $$bEdge[$i] or return 0;
798             }
799              
800 271         687 1
801             }
802              
803              
804             sub equivalent
805             {
806 81     81 1 290 my($a, $set_spec) = @_;
807 81         163 my $b = $a->_real_set($set_spec);
808              
809 81         701 $a->cardinality == $b->cardinality
810             }
811              
812              
813             sub cardinality
814             {
815 198     198 1 246 my $set = shift;
816              
817 198 100 100     864 ($set->{negInf} or $set->{posInf}) and return -1;
818              
819 138         196 my $cardinality = 0;
820 138         148 my @edges = @{$set->{edges}};
  138         358  
821 138         369 while (@edges)
822             {
823 157         196 my $lower = shift @edges;
824 157         194 my $upper = shift @edges;
825 157         384 $cardinality += $upper - $lower;
826             }
827              
828             $cardinality
829 138         353 }
830              
831             *size = \&cardinality;
832              
833              
834             sub empty
835             {
836 1302     1302 1 1428 my $set = shift;
837              
838 1302   66     3890 not $set->{negInf} and not @{$set->{edges}} and not $set->{posInf}
839             }
840              
841              
842             sub finite
843             {
844 27     27 1 97 my $set = shift;
845              
846 27   100     131 not $set->{negInf} and not $set->{posInf}
847             }
848              
849              
850 162     162 1 1093 sub neg_inf { shift->{negInf} }
851 242     242 1 1510 sub pos_inf { shift->{posInf} }
852              
853              
854             sub infinite
855             {
856 9     9 1 35 my $set = shift;
857              
858 9 100       40 $set->{negInf} or $set->{posInf}
859             }
860              
861              
862             sub universal
863             {
864 9     9 1 40 my $set = shift;
865              
866 9 100 100     613 $set->{negInf} and not @{$set->{edges}} and $set->{posInf}
  2         15  
867             }
868              
869              
870             sub member
871             {
872 97     97 1 371 my($set, $n) = @_;
873              
874 97         203 my $i = _bsearch($set->{edges}, $n);
875              
876 97   100     541 $set->{negInf} xor $i & 1
877             }
878              
879 20     20   136335 use constant INSERT => 0;
  20         50  
  20         11856  
880 20     20   123 use constant REMOVE => 1;
  20         39  
  20         104905  
881              
882 461     461 1 1426 sub insert { _indel(@_, INSERT); }
883 49     49 1 247 sub remove { _indel(@_, REMOVE); }
884              
885             sub _indel # INsertion/DELetion
886             {
887 510     510   708 my($set, $n, $indel) = @_;
888 510 50       1170 defined $n or return;
889              
890 510         739 my $edge = $set->{edges};
891 510         1067 my $i = _bsearch($edge, $n);
892              
893 510 100 100     4238 return if $set->{negInf} xor $i & 1 xor $indel;
      100        
894              
895 423   100     1422 my $lGap = $i==0 || $edge->[$i-1] < $n-1;
896 423   100     1117 my $rGap = $i==@$edge || $n < $edge->[$i];
897              
898 423 100 100     2198 if ( $lGap and $rGap) { splice @$edge, $i, 0, $n-1, $n }
  81 100 100     497  
    100 66        
899 284         1073 elsif (not $lGap and $rGap) { $edge->[$i-1]++ }
900 55         253 elsif ( $lGap and not $rGap) { $edge->[$i ]-- }
901 3         21 else { splice @$edge, $i-1, 2 }
902             }
903              
904             # Returns the index of the first edge that satisifies target <= edge.
905             # Returns $#$edges+1 if target > the last edge.
906             # Returns 0 if edges is empty.
907             sub _bsearch
908             {
909 756     756   2259 my($edges, $target) = @_;
910              
911 756 100       1526 @$edges or return 0;
912              
913 681         721 my $lower = 0;
914 681         854 my $upper = $#$edges;
915              
916 681         1694 while ($lower+1 < $upper)
917             {
918 727         1133 my $mid = int(($lower + $upper) / 2);
919              
920 727 100       1919 if ($target <= $edges->[$mid])
921             {
922 264         586 $upper = $mid;
923             }
924             else
925             {
926 463         1172 $lower = $mid+1;
927             }
928             }
929              
930 681 100       1712 $target <= $edges->[$lower] and return $lower;
931 493 100       1058 $target <= $edges->[$upper] and return $upper;
932 358         645 $upper + 1
933             }
934              
935             sub span_ord
936             {
937 24     24 1 87 my($set, $n) = @_;
938              
939 24         49 my $i = _bsearch($set->{edges}, $n);
940 24 100 100     144 ($set->{negInf} xor $i & 1) ? $i >> 1 : undef
941             }
942              
943             sub min
944             {
945 26     26 1 56 my $set = shift;
946              
947 26 100       50 $set->empty and return undef;
948 23 100       156 $set->neg_inf and return undef;
949 19         52 $set->{edges}->[0]+1
950             }
951              
952              
953             sub max
954             {
955 26     26 1 52 my $set = shift;
956              
957 26 100       45 $set->empty and return undef;
958 23 100       132 $set->pos_inf and return undef;
959 19         56 $set->{edges}->[-1]
960             }
961              
962             sub cover
963             {
964 13     13 1 185 my $set = shift;
965 13         20 my $cover = $set->new();
966 13         19 my $edges = $set->{edges};
967 13         19 my $negInf = $set->{negInf};
968 13         16 my $posInf = $set->{posInf};
969              
970 13 100 100     522 if ($negInf and $posInf)
    100 66        
    100 66        
    100          
971             {
972 2         3 $cover->{negInf} = 1;
973 2         4 $cover->{posInf} = 1;
974             }
975             elsif ($negInf and not $posInf)
976             {
977 2         4 $cover->{negInf} = 1;
978 2         6 $cover->{edges}[0] = $set->{edges}[-1];
979             }
980             elsif (not $negInf and $posInf)
981             {
982 2         6 $cover->{edges}[0] = $set->{edges}[0];
983 2         5 $cover->{posInf} = 1;
984             }
985             elsif (@$edges)
986             {
987 5         10 $cover->{edges}[0] = $set->{edges}[ 0];
988 5         10 $cover->{edges}[1] = $set->{edges}[-1];
989             }
990              
991             $cover
992 13         28 }
993              
994             *extent = \&cover;
995              
996              
997             sub holes
998             {
999 13     13 1 32 my $set = shift;
1000 13         18 my $holes = $set->new($set);
1001 13         16 my $edges = $holes->{edges};
1002 13         15 my $negInf = $holes->{negInf};
1003 13         13 my $posInf = $holes->{posInf};
1004              
1005 13 100 100     82 if ($negInf and $posInf)
    100 66        
    100 66        
    100          
1006             {
1007 2         3 $holes->{negInf} = 0;
1008 2         3 $holes->{posInf} = 0;
1009             }
1010             elsif ($negInf and not $posInf)
1011             {
1012 2         3 $holes->{negInf} = 0;
1013 2         3 pop @$edges;
1014             }
1015             elsif (not $negInf and $posInf)
1016             {
1017 2         4 shift @$edges;
1018 2         4 $holes->{posInf} = 0;
1019             }
1020             elsif (@$edges)
1021             {
1022 5         6 shift @$edges;
1023 5         6 pop @$edges;
1024             }
1025              
1026             $holes
1027 13         24 }
1028              
1029             sub inset
1030             {
1031 37     37 1 123 my($set, $n) = @_;
1032 37         47 my $edges = $set->{edges};
1033 37         93 my @edges = @$edges;
1034              
1035 37         53 my $inset = $set->new();
1036 37         56 $inset->{negInf} = $set->{negInf};
1037 37         54 $inset->{posInf} = $set->{posInf};
1038              
1039 37         35 my @inset;
1040 37         41 my $nAbs = abs $n;
1041              
1042 37 100 100     251 if (@edges and ($inset->{negInf} xor $n < 0))
      100        
1043             {
1044 13         19 my $edge = shift @edges;
1045 13         27 push @inset, $edge - $nAbs;
1046             }
1047              
1048 37         88 while (@edges > 1)
1049             {
1050 79         151 my($lower, $upper) = splice(@edges, 0, 2);
1051 79         137 $lower += $nAbs;
1052 79         72 $upper -= $nAbs;
1053              
1054 79 100       225 push @inset, $lower, $upper
1055             if $lower < $upper;
1056             }
1057              
1058 37 100       64 if (@edges)
1059             {
1060 13         19 my $edge = shift @edges;
1061 13         24 push @inset, $edge + $nAbs;
1062             }
1063              
1064              
1065 37         60 $inset->{edges} = \@inset;
1066 37         135 $inset
1067             }
1068              
1069             *trim = \&inset;
1070              
1071             sub pad
1072             {
1073 1     1 1 3 my($set, $n) = @_;
1074 1         4 $set->inset(-$n)
1075             }
1076              
1077              
1078             sub grep_set(&$)
1079             {
1080 45     45 1 266 my($block, $set) = @_;
1081              
1082 45 100 100     578 return undef if $set->{negInf} or $set->{posInf};
1083              
1084 30         34 my @edges = @{$set->{edges}};
  30         91  
1085 30         44 my @sub_edges = ();
1086              
1087 30         66 while (@edges)
1088             {
1089 35         310 my($lower, $upper) = splice(@edges, 0, 2);
1090              
1091 35         695 for (my $i=$lower+1; $i<=$upper; $i++)
1092             {
1093 150         3411 local $_ = $i;
1094 150 100       467 &$block() or next;
1095              
1096 60 100 100     3181 if (@sub_edges and $sub_edges[-1] == $i-1)
1097             {
1098 29         87 $sub_edges[-1] = $i;
1099             }
1100             else
1101             {
1102 31         127 push @sub_edges, $i-1, $i;
1103             }
1104             }
1105             }
1106              
1107 30         883 my $sub_set = $set->new;
1108 30         81 $sub_set->{edges} = \@sub_edges;
1109 30         109 $sub_set
1110             }
1111              
1112              
1113             sub map_set(&$)
1114             {
1115 63     63 1 368 my($block, $set) = @_;
1116              
1117 63 100 100     381 return undef if $set->{negInf} or $set->{posInf};
1118              
1119 42         84 my $map_set = $set->new;
1120              
1121 42         52 my @edges = @{$set->{edges}};
  42         214  
1122 42         235 while (@edges)
1123             {
1124 49         179 my($lower, $upper) = splice(@edges, 0, 2);
1125              
1126 49         225 my $domain;
1127 49         190 for ($domain=$lower+1; $domain<=$upper; $domain++)
1128             {
1129 210         824 local $_ = $domain;
1130              
1131 210         215 my $range;
1132 210         479 for $range (&$block())
1133             {
1134 210         9988 $map_set->insert($range);
1135             }
1136             }
1137             }
1138              
1139             $map_set
1140 42         330 }
1141              
1142              
1143             sub grep_spans(&$)
1144             {
1145 40     40 1 216 my($block, $set) = @_;
1146              
1147 40         42 my @edges = @{$set->{edges}};
  40         102  
1148 40         79 my $sub_set = $set->new;
1149 40         50 my @sub_edges = ();
1150              
1151 40 100 100     153 if ($set->{negInf} and $set->{posInf})
    100          
1152             {
1153 4         7 local $_ = [ undef, undef ];
1154 4 100       9 if (&$block())
1155             {
1156 2         110 $sub_set->{negInf} = 1;
1157 2         5 $sub_set->{posInf} = 1;
1158             }
1159             }
1160             elsif ($set->{negInf})
1161             {
1162 4         7 my $upper = shift @edges;
1163 4         10 local $_ = [ undef, $upper ];
1164 4 100       8 if (&$block())
1165             {
1166 2         83 $sub_set->{negInf} = 1;
1167 2         7 push @sub_edges, $upper;
1168             }
1169             }
1170              
1171 40         292 while (@edges > 1)
1172             {
1173 40         790 my($lower, $upper) = splice(@edges, 0, 2);
1174 40         98 local $_ = [ $lower+1, $upper ];
1175 40 100       83 &$block() and push @sub_edges, $lower, $upper;
1176             }
1177              
1178 40 100       1159 if (@edges)
1179             {
1180 8         11 my $lower = shift @edges;
1181 8         16 local $_ = [ $lower+1, undef ];
1182 8 100       19 if (&$block())
1183             {
1184 4         153 $sub_set->{posInf} = 1;
1185 4         11 push @sub_edges, $lower;
1186             }
1187             }
1188              
1189 40         234 $sub_set->{edges} = \@sub_edges;
1190 40         108 $sub_set
1191             }
1192              
1193             sub map_spans(&$)
1194             {
1195 60     60 1 324 my($block, $set) = @_;
1196              
1197 60         59 my @edges = @{$set->{edges}};
  60         154  
1198 60         64 my @spans;
1199              
1200 60 100 100     233 if ($set->{negInf} and $set->{posInf})
    100          
1201             {
1202 6         13 local $_ = [ undef, undef ];
1203 6         15 push @spans, &$block();
1204             }
1205             elsif ($set->{negInf})
1206             {
1207 6         10 my $upper = shift @edges;
1208 6         13 local $_ = [ undef, $upper ];
1209 6         14 push @spans, &$block();
1210             }
1211              
1212 60         764 while (@edges > 1)
1213             {
1214 60         1109 my($lower, $upper) = splice(@edges, 0, 2);
1215 60         132 local $_ = [ $lower+1, $upper ];
1216 60         113 push @spans, &$block();
1217             }
1218              
1219 60 100       1694 if (@edges)
1220             {
1221 12         17 my $lower = shift @edges;
1222 12         27 local $_ = [ $lower+1, undef ];
1223 12         26 push @spans, &$block();
1224             }
1225              
1226 60         655 $set->new->_insert_spans(\@spans)
1227             }
1228              
1229              
1230             sub first($)
1231             {
1232 17     17 1 64 my $set = shift;
1233              
1234 17         35 $set->{iterator} = $set->min;
1235 17         42 $set->{run}[0] = 0;
1236 17 100       22 $set->{run}[1] = $#{$set->{edges}} ? 1 : undef;
  17         52  
1237              
1238 17         45 $set->{iterator}
1239             }
1240              
1241              
1242             sub last($)
1243             {
1244 17     17 1 65 my $set = shift;
1245              
1246 17         21 my $lastEdge = $#{$set->{edges}};
  17         36  
1247 17         82 $set->{iterator} = $set->max;
1248 17 100       53 $set->{run}[0] = $lastEdge ? $lastEdge-1 : undef;
1249 17         27 $set->{run}[1] = $lastEdge;
1250              
1251 17         48 $set->{iterator}
1252             }
1253              
1254              
1255             sub start($$)
1256             {
1257 58     58 1 898 my($set, $start) = @_;
1258              
1259 58         92 $set->{iterator} = undef;
1260 58 50       110 defined $start or return undef;
1261              
1262 58         81 my $inSet = $set->{negInf};
1263 58         67 my $edges = $set->{edges};
1264              
1265 58         136 for (my $i=0; $i<@$edges; $i++)
1266             {
1267 171 100       4515 if ($inSet)
1268             {
1269 82 100       157 if ($start <= $$edges[$i])
1270             {
1271 29         38 $set->{iterator} = $start;
1272 29 100       69 $set->{run}[0] = $i ? $i-1 : undef;
1273 29         41 $set->{run}[1] = $i;
1274 29         142 return $start;
1275             }
1276 53         116 $inSet = 0;
1277             }
1278             else
1279             {
1280 89 100       269 if ($start <= $$edges[$i])
1281             {
1282 18         55 return undef;
1283             }
1284 71         146 $inSet = 1;
1285             }
1286             }
1287              
1288 11 100       23 if ($inSet)
1289             {
1290 8         15 $set->{iterator} = $start;
1291 8 100       20 $set->{run}[0] = @$edges? $#$edges: undef;
1292 8         13 $set->{run}[1] = undef;
1293             }
1294              
1295 11         27 $set->{iterator}
1296             }
1297              
1298              
1299 11     11 1 50 sub current($) { shift->{iterator} }
1300              
1301              
1302             sub next($)
1303             {
1304 44     44 1 319 my $set = shift;
1305              
1306 44 100       123 defined $set->{iterator} or return $set->first;
1307              
1308 42         55 my $run1 = $set->{run}[1];
1309 42 100       89 defined $run1 or return ++$set->{iterator};
1310              
1311 41         55 my $edges = $set->{edges};
1312 41 100       120 $set->{iterator} < $edges->[$run1] and return ++$set->{iterator};
1313              
1314 13 100       36 if ($run1 < $#$edges-1)
    100          
1315             {
1316 4         8 my $run0 = $run1 + 1;
1317 4         9 $set->{run} = [$run0, $run0+1];
1318 4         11 $set->{iterator} = $edges->[$run0]+1;
1319             }
1320             elsif ($run1 < $#$edges)
1321             {
1322 2         4 my $run0 = $run1 + 1;
1323 2         5 $set->{run} = [$run0, undef];
1324 2         6 $set->{iterator} = $edges->[$run0]+1;
1325             }
1326             else
1327             {
1328 7         16 $set->{iterator} = undef;
1329             }
1330              
1331 13         35 $set->{iterator}
1332             }
1333              
1334              
1335             sub prev($)
1336             {
1337 39     39 1 267 my $set = shift;
1338              
1339 39 100       96 defined $set->{iterator} or return $set->last;
1340              
1341 37         49 my $run0 = $set->{run}[0];
1342 37 100       71 defined $run0 or return --$set->{iterator};
1343              
1344 36         52 my $edges = $set->{edges};
1345 36 100       128 $set->{iterator} > $edges->[$run0]+1 and return --$set->{iterator};
1346              
1347 11 100       27 if ($run0 > 1)
    100          
1348             {
1349 3         6 my $run1 = $run0 - 1;
1350 3         9 $set->{run} = [$run1-1, $run1];
1351 3         9 $set->{iterator} = $edges->[$run1];
1352             }
1353             elsif ($run0 > 0)
1354             {
1355 1         3 my $run1 = $run0 - 1;
1356 1         4 $set->{run} = [undef, $run1];
1357 1         4 $set->{iterator} = $edges->[$run1];
1358             }
1359             else
1360             {
1361 7         12 $set->{iterator} = undef;
1362             }
1363              
1364 11         31 $set->{iterator}
1365             }
1366              
1367             sub at
1368             {
1369 29     29 1 36 my($set, $i) = @_;
1370              
1371 29 100       89 $i < 0 ? $set->_at_neg($i) : $set->_at_pos($i)
1372             }
1373              
1374             sub _at_pos
1375             {
1376 14     14   16 my($set, $i) = @_;
1377              
1378 14 100       23 $set->neg_inf and
1379             croak "Set::IntSpan::at: negative infinite set\n";
1380              
1381 13         13 my @edges = @{$set->{edges}};
  13         37  
1382              
1383 13         33 while (@edges > 1)
1384             {
1385 16         28 my($lower, $upper) = splice(@edges, 0, 2);
1386              
1387 16         34 my $size = $upper - $lower;
1388              
1389 16 100       47 $i < $size and return $lower + 1 + $i;
1390              
1391 10         29 $i -= $size;
1392             }
1393              
1394 7 100       32 @edges ? $edges[0] + 1 + $i : undef
1395             }
1396              
1397             sub _at_neg
1398             {
1399 15     15   17 my($set, $i) = @_;
1400              
1401 15 100       27 $set->pos_inf and
1402             croak "Set::IntSpan::at: positive infinite set\n";
1403              
1404 14         15 my @edges = @{$set->{edges}};
  14         67  
1405 14         17 $i++;
1406              
1407 14         30 while (@edges > 1)
1408             {
1409 19         31 my($lower, $upper) = splice(@edges, -2, 2);
1410              
1411 19         28 my $size = $upper - $lower;
1412              
1413 19 100       575 -$i < $size and return $upper + $i;
1414              
1415 12         28 $i += $size;
1416             }
1417              
1418 7 100       32 @edges ? $edges[0] + $i : undef
1419             }
1420              
1421             sub ord
1422             {
1423 16     16 1 18 my($set, $n) = @_;
1424              
1425 16 100       281 $set->{negInf} and
1426             croak "Set::IntSpan::ord: negative infinite set\n";
1427              
1428 15 50       30 defined $n or return undef;
1429              
1430 15         13 my $i = 0;
1431 15         18 my @edges = @{$set->{edges}};
  15         39  
1432              
1433 15         30 while (@edges)
1434             {
1435 21         33 my($lower, $upper) = splice(@edges, 0, 2);
1436              
1437 21 100       55 $n <= $lower and return undef;
1438              
1439 17 100 100     71 if (defined $upper and $upper < $n)
1440             {
1441 9         10 $i += $upper - $lower;
1442 9         30 next;
1443             }
1444              
1445 8         31 return $i + $n - $lower - 1;
1446             }
1447              
1448             undef
1449 3         12 }
1450              
1451             sub slice
1452             {
1453 35     35 1 55 my($set, $from, $to) = @_;
1454              
1455 35         71 $set->{slicing} = 1;
1456 35         130 my $slice = $set->_splice($from, $to - $from + 1);
1457 33         59 $set->{slicing} = 0;
1458              
1459 33         74 $slice
1460             }
1461              
1462             sub _splice
1463             {
1464 233     233   393 my($set, $offset, $length) = @_;
1465              
1466 233 100       617 $offset < 0
1467             ? $set->_splice_neg($offset, $length)
1468             : $set->_splice_pos($offset, $length)
1469             }
1470              
1471             sub _splice_pos
1472             {
1473 116     116   139 my($set, $offset, $length) = @_;
1474              
1475 116 100       175 $set->neg_inf and
1476             croak "Set::IntSpan::slice: negative infinite set\n";
1477              
1478 114         133 my @edges = @{$set->{edges}};
  114         287  
1479 114         227 my $slice = new Set::IntSpan;
1480              
1481 114         232 while (@edges > 1)
1482             {
1483 151         275 my ($lower, $upper) = @edges[0,1];
1484 151         163 my $size = $upper - $lower;
1485              
1486 151 100       276 $offset < $size and last;
1487              
1488 62         79 splice(@edges, 0, 2);
1489 62         138 $offset -= $size;
1490             }
1491              
1492             @edges or
1493 114 100       271 return $slice; # empty set
1494              
1495 102         115 $edges[0] += $offset;
1496              
1497 102         180 $slice->{edges} = $set->_splice_length(\@edges, $length);
1498 101         368 $slice
1499             }
1500              
1501             sub _splice_neg
1502             {
1503 117     117   143 my($set, $offset, $length) = @_;
1504              
1505 117 100       207 $set->pos_inf and
1506             croak "Set::IntSpan::slice: positive infinite set\n";
1507              
1508 114         124 my @edges = @{$set->{edges}};
  114         379  
1509 114         479 my $slice = new Set::IntSpan;
1510              
1511 114         140 my @slice;
1512 114         120 $offset++;
1513              
1514 114         224 while (@edges > 1)
1515             {
1516 193         345 my ($lower, $upper) = @edges[-2,-1];
1517 193         240 my $size = $upper - $lower;
1518              
1519 193 100       697 -$offset < $size and last;
1520              
1521 101         233 unshift @slice, splice(@edges, -2, 2);
1522 101         257 $offset += $size;
1523             }
1524              
1525 114 100       227 if (@edges)
    100          
1526             {
1527 100         139 my $upper = pop @edges;
1528 100         261 unshift @slice, $upper+$offset-1, $upper;
1529             }
1530             elsif ($set->{slicing})
1531             {
1532 2         4 $length += $offset-1;
1533             }
1534              
1535 114         271 $slice->{edges} = $set->_splice_length(\@slice, $length);
1536 114         405 $slice
1537             }
1538              
1539             sub _splice_length
1540             {
1541 216     216   276 my($set, $edges, $length) = @_;
1542              
1543 216 100       479 not defined $length and return $edges; # everything
1544 193 100       445 $length<0 and return $set->_splice_length_neg($edges, -$length);
1545 115 100       297 $length>0 and return $set->_splice_length_pos($edges, $length);
1546              
1547 12         145 [] # $length==0
1548             }
1549              
1550             sub _splice_length_pos
1551             {
1552 103     103   113 my($set, $edges, $length) = @_;
1553              
1554 103         91 my @slice;
1555              
1556 103         203 while (@$edges > 1)
1557             {
1558 125         206 my ($lower, $upper) = @$edges[0,1];
1559 125         129 my $size = $upper - $lower;
1560              
1561 125 100       214 $length <= $size and last;
1562              
1563 54         95 push @slice, splice(@$edges, 0, 2);
1564 54         131 $length -= $size;
1565             }
1566              
1567 103 100       175 if (@$edges)
1568             {
1569 84         98 my $lower = shift @$edges;
1570 84         141 push @slice, $lower, $lower+$length;
1571             }
1572              
1573             \@slice
1574 103         264 }
1575              
1576             sub _splice_length_neg
1577             {
1578 78     78   94 my($set, $edges, $length) = @_;
1579              
1580 78 100       136 $set->pos_inf and
1581             croak "Set::IntSpan::slice: positive infinite set\n";
1582              
1583 77         178 while (@$edges > 1)
1584             {
1585 126         199 my($lower, $upper) = @$edges[-2,-1];
1586 126         150 my $size = $upper - $lower;
1587              
1588 126 100       227 $length < $size and last;
1589              
1590 73         98 splice(@$edges, -2, 2);
1591 73         172 $length -= $size;
1592             }
1593              
1594 77 100       141 if (@$edges)
1595             {
1596 53         73 $edges->[-1] -= $length;
1597             }
1598              
1599             $edges
1600 77         178 }
1601              
1602             1
1603              
1604             __END__