File Coverage

blib/lib/AlignDB/IntSpanXS.pm
Criterion Covered Total %
statement 388 453 85.6
branch 100 130 76.9
condition 28 39 71.7
subroutine 63 78 80.7
pod 0 58 0.0
total 579 758 76.3


line stmt bran cond sub pod time code
1             package AlignDB::IntSpanXS;
2 13     13   179148 use strict;
  13         32  
  13         337  
3 13     13   46 use warnings;
  13         14  
  13         358  
4              
5 13     13   44 use base qw( DynaLoader );
  13         19  
  13         1440  
6 13     13   53 use Carp;
  13         16  
  13         952  
7 13     13   57 use Scalar::Util qw(blessed);
  13         18  
  13         905  
8 13     13   5174 use Scalar::Util::Numeric qw(isint);
  13         6503  
  13         1221  
9              
10             use overload (
11 0     0   0 q{0+} => sub { confess "Can't numerify an AlignDB::IntSpanXS\n" },
12 0     0   0 q{bool} => sub { confess "Can't bool an AlignDB::IntSpanXS\n" },
13 13         100 q{""} => q{runlist},
14              
15             # use Perl standard behaviours for other operations
16             fallback => 1,
17 13     13   12397 );
  13         9571  
18              
19             BEGIN {
20 13     13   999 our $VERSION = '1.0.2';
21 13         41713 bootstrap AlignDB::IntSpanXS, $VERSION;
22             }
23              
24             # POS_INF
25             # NEG_INF
26             # EMPTY_STRING
27              
28             sub new {
29 11809     11809 0 3494835 my $class = shift;
30 11809         32328 my $self = _new($class);
31 11809 100       20627 $self->add(@_) if @_ > 0;
32 11809         17674 return $self;
33             }
34              
35             sub valid {
36 0     0 0 0 my $this = shift;
37 0         0 my $runlist = shift;
38              
39 0   0     0 my $class = ref($this) || $this;
40 0         0 my $set = new $class;
41              
42 0         0 eval { $set->_runlist_to_ranges($runlist) };
  0         0  
43 0 0       0 return $@ ? 0 : 1;
44             }
45              
46             # clear
47              
48             sub edges_ref {
49 126     126 0 68 my $self = shift;
50 126         323 return [ $self->edges ];
51             }
52              
53             # edges
54             # edge_size
55             # span_size
56             # as_string
57             # as_array
58             # ranges
59              
60             sub spans {
61 19     19 0 42 my $self = shift;
62              
63 19         18 my @spans;
64 19         85 my @ranges = $self->ranges;
65 19         31 while (@ranges) {
66 20         19 my $lower = shift @ranges;
67 20         13 my $upper = shift @ranges;
68 20         45 push @spans, [ $lower, $upper ];
69             }
70              
71 19         30 return @spans;
72             }
73              
74             sub sets {
75 128     128 0 112 my $self = shift;
76              
77 128         93 my @sets;
78 128         326 my @ranges = $self->ranges;
79 128         186 while (@ranges) {
80 163         130 my $lower = shift @ranges;
81 163         104 my $upper = shift @ranges;
82 163         485 push @sets, blessed($self)->new("$lower-$upper");
83             }
84              
85 128         199 return @sets;
86             }
87              
88             sub runlists {
89 19     19 0 39 my $self = shift;
90              
91 19 100       48 if ( $self->is_empty ) {
92 2         7 return $self->EMPTY_STRING;
93             }
94              
95 17         14 my @runlists;
96 17         49 my @ranges = $self->ranges;
97 17         29 while (@ranges) {
98 20         32 my $lower = shift @ranges;
99 20         13 my $upper = shift @ranges;
100 20 100       37 my $string = $lower == $upper ? $lower : $lower . '-' . $upper;
101 20         36 push @runlists, $string;
102             }
103              
104 17         34 return @runlists;
105             }
106              
107             # cardinality
108             # is_empty
109             # is_not_empty
110             # is_neg_inf
111             # is_pos_inf
112             # is_infinite
113             # is_finite
114             # is_universal
115              
116             sub contains_all {
117 195     195 0 168 my $self = shift;
118              
119 195         131 for my $i (@_) {
120 195 100       364 return 0 unless $self->_contains($i);
121             }
122              
123 100         81 return 1;
124             }
125              
126             sub contains_any {
127 3     3 0 6 my $self = shift;
128              
129 3         5 for my $i (@_) {
130 7 100       19 return 1 if $self->_contains($i);
131             }
132              
133 2         5 return 0;
134             }
135              
136             # add_pair
137             # add_int
138             # add_array
139             # add_runlist
140              
141             sub add_range {
142 30444     30444 0 692350 my $self = shift;
143 30444         30102 my @ranges = @_;
144              
145 30444 50       42063 if ( scalar(@ranges) % 2 == 1 ) {
146 0         0 confess "Number of ranges must be even: @ranges\n";
147             }
148              
149 30444         37949 while (@ranges) {
150 37983         27231 my $from = shift @ranges;
151 37983         24095 my $to = shift @ranges;
152 37983         85730 $self->add_pair( $from, $to );
153             }
154              
155 30444         28932 return $self;
156             }
157              
158             sub add {
159 8099     8099 0 13327 my $self = shift;
160 8099         5570 my $first = shift;
161              
162 8099 100       12464 if ( ref $first eq __PACKAGE__ ) {
    100          
163 6447         13999 $self->add_range( $first->ranges );
164             }
165             elsif ( isint($first) ) {
166 806 100       1024 if ( scalar @_ > 0 ) {
167 38         215 $self->add_array( [ $first, @_ ] );
168             }
169             else {
170 768         1565 $self->add_int($first);
171             }
172             }
173             else {
174 846         3944 $self->add_runlist($first);
175             }
176              
177 8099         7309 return $self;
178             }
179              
180             # invert
181             # remove_pair
182             # remove_int
183             # remove_array
184             # remove_runlist
185              
186             sub remove_range {
187 2455     2455 0 597806 my $self = shift;
188              
189 2455         5854 $self->invert;
190 2455         3814 $self->add_range(@_);
191 2455         3765 $self->invert;
192              
193 2455         2734 return $self;
194             }
195              
196             sub remove {
197 861     861 0 1956 my $self = shift;
198 861         576 my $first = shift;
199              
200 861 50       1571 if ( ref $first eq __PACKAGE__ ) {
    0          
201 861         3746 $self->remove_range( $first->ranges );
202             }
203             elsif ( isint($first) ) {
204 0 0       0 if ( scalar @_ > 0 ) {
205 0         0 $self->remove_array( [ $first, @_ ] );
206             }
207             else {
208 0         0 $self->remove_int($first);
209             }
210             }
211             else {
212 0         0 $self->remove_runlist($first);
213             }
214              
215 861         2287 return $self;
216             }
217              
218             sub merge {
219 171     171 0 1013 my $self = shift;
220              
221 171         181 foreach my $supplied (@_) {
222 177         215 my @ranges = $self->_real_set($supplied)->ranges;
223 177         254 $self->add_range(@ranges);
224             }
225              
226 171         277 return $self;
227             }
228              
229             sub subtract {
230 167     167 0 107 my $self = shift;
231              
232 167         197 foreach my $supplied (@_) {
233 167         232 my @ranges = $self->_real_set($supplied)->ranges;
234 167         246 $self->remove_range(@ranges);
235             }
236              
237 167         116 return $self;
238             }
239              
240             # copy
241              
242             sub union {
243 33     33 0 2886 my $self = shift;
244              
245 33         97 my $new = $self->copy;
246 33         53 $new->merge(@_);
247              
248 33         46 return $new;
249             }
250              
251             sub complement {
252 231     231 0 149 my $self = shift;
253              
254 231         510 my $new = $self->copy;
255 231         320 $new->invert;
256              
257 231         198 return $new;
258             }
259              
260             sub diff {
261 167     167 0 148 my $self = shift;
262              
263 167         437 my $new = $self->copy;
264 167         232 $new->subtract(@_);
265              
266 167         387 return $new;
267             }
268              
269             sub intersect {
270 102     102 0 136 my $self = shift;
271              
272 102         134 my $new = $self->complement;
273 102         114 for my $supplied (@_) {
274 106         131 my $temp_set = $self->_real_set($supplied)->complement;
275 106         136 $new->merge($temp_set);
276             }
277 102         159 $new->invert;
278              
279 102         144 return $new;
280             }
281              
282             sub xor {
283 10     10 0 33 return intersect( union(@_), intersect(@_)->complement );
284             }
285              
286             sub equal {
287 115     115 0 383 my $self = shift;
288              
289 115         179 for (@_) {
290 128         171 my $supplied = $self->_real_set($_);
291              
292 128 100       429 if ( $self->edge_size != $supplied->edge_size ) {
293 40         76 return 0;
294             }
295              
296 88         179 my @edges_a = $self->edges;
297 88         141 my @edges_b = $supplied->edges;
298              
299 88         204 for ( my $i = 0; $i < $self->edge_size; $i++ ) {
300 268 100       624 if ( $edges_a[$i] != $edges_b[$i] ) {
301 18         37 return 0;
302             }
303             }
304             }
305              
306 57         126 return 1;
307             }
308              
309             sub subset {
310 74     74 0 107 my $self = shift;
311 74         98 my $supplied = $self->_real_set(shift);
312              
313 74         100 return $self->diff($supplied)->is_empty;
314             }
315              
316             sub superset {
317 74     74 0 364 my $self = shift;
318 74         125 my $supplied = $self->_real_set(shift);
319              
320 74         139 return $supplied->diff($self)->is_empty;
321             }
322              
323             sub smaller_than {
324 36     36 0 70 my $self = shift;
325 36         33 my $supplied = shift;
326              
327 36   100     45 my $result = $self->subset($supplied) && !$self->equal($supplied);
328              
329 36 100       103 return $result ? 1 : 0;
330             }
331              
332             sub larger_than {
333 36     36 0 76 my $self = shift;
334 36         23 my $supplied = shift;
335              
336 36   100     47 my $result = $self->superset($supplied) && !$self->equal($supplied);
337              
338 36 100       111 return $result ? 1 : 0;
339             }
340              
341             sub at {
342 21     21 0 49 my $self = shift;
343 21         15 my $index = shift;
344 21 100 66     108 if ( $index == 0 || abs($index) > $self->cardinality ) {
345 8         26 return;
346             }
347 13 100       31 my $member
348             = $index < 0 ? $self->_at_neg( -$index ) : $self->_at_pos($index);
349 13         38 return $member;
350             }
351              
352             sub _at_pos {
353 6     6   5 my $self = shift;
354 6         6 my $index = shift;
355              
356 6         4 my $member;
357 6         4 my $element_before = 0;
358              
359 6         32 my @ranges = $self->ranges;
360 6         32 while (@ranges) {
361 9         11 my $lower = shift @ranges;
362 9         9 my $upper = shift @ranges;
363 9         8 my $span_size = $upper - $lower + 1;
364              
365 9 100       12 if ( $index > $element_before + $span_size ) {
366 3         4 $element_before += $span_size;
367             }
368             else {
369 6         7 $member = $index - $element_before - 1 + $lower;
370 6         7 last;
371             }
372             }
373              
374 6         7 return $member;
375             }
376              
377             sub _at_neg {
378 7     7   8 my $self = shift;
379 7         3 my $index = shift;
380              
381 7         5 my $member;
382 7         5 my $element_after = 0;
383              
384 7         24 my @r_ranges = reverse $self->ranges;
385 7         12 while (@r_ranges) {
386 12         19 my $upper = shift @r_ranges;
387 12         7 my $lower = shift @r_ranges;
388 12         10 my $span_size = $upper - $lower + 1;
389              
390 12 100       15 if ( $index > $element_after + $span_size ) {
391 5         5 $element_after += $span_size;
392             }
393             else {
394 7         5 $member = $upper - ( $index - $element_after ) + 1;
395 7         7 last;
396             }
397             }
398              
399 7         8 return $member;
400             }
401              
402             sub index {
403 11     11 0 23 my $self = shift;
404 11         9 my $member = shift;
405              
406 11         7 my $index;
407 11         7 my $element_before = 0;
408              
409 11         43 my @ranges = $self->ranges;
410 11         19 while (@ranges) {
411 16         13 my $lower = shift @ranges;
412 16         10 my $upper = shift @ranges;
413 16         14 my $span_size = $upper - $lower + 1;
414              
415 16 100 100     47 if ( $member >= $lower and $member <= $upper ) {
416 8         6 $index = $member - $lower + 1 + $element_before;
417 8         9 last;
418             }
419             else {
420 8         13 $element_before += $span_size;
421             }
422             }
423              
424 11         39 return $index;
425             }
426              
427             sub slice {
428 11     11 0 26 my $self = shift;
429 11         9 my $from = shift;
430 11         5 my $to = shift;
431              
432 11 100       19 if ( $from < 1 ) {
433 2         208 carp "Start index less than 1\n";
434 2         195 $from = 1;
435             }
436 11         21 my $slice = $self->_splice( $from, $to - $from + 1 );
437              
438 11         48 return $slice;
439             }
440              
441             sub _splice {
442 56     56   110 my $self = shift;
443 56         37 my $offset = shift;
444 56         44 my $length = shift;
445              
446 56         126 my @edges = $self->edges;
447 56         175 my $slice = blessed($self)->new;
448              
449 56         98 while ( @edges > 1 ) {
450 75         86 my ( $lower, $upper ) = @edges[ 0, 1 ];
451 75         46 my $span_size = $upper - $lower;
452              
453 75 100       84 if ( $offset <= $span_size ) {
454 45         48 last;
455             }
456             else {
457 30         33 splice( @edges, 0, 2 );
458 30         41 $offset -= $span_size;
459             }
460             }
461              
462             @edges
463 56 100       108 or return $slice; # empty set
464              
465 45         44 $edges[0] += $offset - 1;
466              
467 45         65 my @slices = $self->_splice_length( \@edges, $length );
468 45         63 while (@slices) {
469 67         44 my $lower = shift @slices;
470 67         47 my $upper = shift(@slices) - 1;
471 67         166 $slice->add_pair( $lower, $upper );
472             }
473              
474 45         200 return $slice;
475             }
476              
477             sub _splice_length {
478 45     45   26 my $self = shift;
479 45         37 my $edges_ref = shift;
480 45         33 my $length = shift;
481              
482 45 100       55 if ( !defined $length ) {
483 8         7 return @{$edges_ref}; # everything
  8         15  
484             }
485              
486 37 100       45 if ( $length <= 0 ) {
487 2         3 return (); # empty
488             }
489              
490 35         26 my @slices;
491              
492 35         44 while ( @$edges_ref > 1 ) {
493 54         49 my ( $lower, $upper ) = @$edges_ref[ 0, 1 ];
494 54         36 my $span_size = $upper - $lower;
495              
496 54 100       59 if ( $length <= $span_size ) {
497 28         19 last;
498             }
499             else {
500 26         30 push @slices, splice( @$edges_ref, 0, 2 );
501 26         34 $length -= $span_size;
502             }
503             }
504              
505 35 100       55 if (@$edges_ref) {
506 28         21 my $lower = shift @$edges_ref;
507 28         32 push @slices, $lower, $lower + $length;
508             }
509              
510 35         57 return @slices;
511             }
512              
513             sub min {
514 63     63 0 47 my $self = shift;
515              
516 63 50       112 if ( $self->is_empty ) {
517 0         0 return;
518             }
519             else {
520 63         93 return $self->edges_ref->[0];
521             }
522             }
523              
524             sub max {
525 63     63 0 49 my $self = shift;
526              
527 63 50       112 if ( $self->is_empty ) {
528 0         0 return;
529             }
530             else {
531 63         65 return $self->edges_ref->[-1] - 1;
532             }
533             }
534              
535             sub grep_set {
536 30     30 0 133 my $self = shift;
537 30         18 my $code_ref = shift;
538              
539 30         24 my @sub_elements;
540 30         42 for ( $self->elements ) {
541 150 100       1868 if ( $code_ref->() ) {
542 60         1864 push @sub_elements, $_;
543             }
544              
545             }
546 30         451 my $sub_set = blessed($self)->new(@sub_elements);
547              
548 30         48 return $sub_set;
549             }
550              
551             sub map_set {
552 42     42 0 171 my $self = shift;
553 42         26 my $code_ref = shift;
554              
555 42         25 my @map_elements;
556 42         51 for ( $self->elements ) {
557 210         454 foreach my $element ( $code_ref->() ) {
558 210 50       4363 if ( defined $element ) {
559 210         349 push @map_elements, $element;
560             }
561             }
562              
563             }
564 42         191 my $map_set = blessed($self)->new(@map_elements);
565              
566 42         66 return $map_set;
567             }
568              
569             sub substr_span {
570 0     0 0 0 my $self = shift;
571 0         0 my $string = shift;
572              
573 0         0 my $sub_string = "";
574 0         0 my @spans = $self->spans;
575              
576 0         0 foreach (@spans) {
577 0         0 my ( $lower, $upper ) = @$_;
578 0         0 my $length = $upper - $lower + 1;
579              
580 0         0 $sub_string .= substr( $string, $lower - 1, $length );
581             }
582              
583 0         0 return $sub_string;
584             }
585              
586             sub banish_span {
587 0     0 0 0 my $self = shift;
588 0         0 my $start = shift;
589 0         0 my $end = shift;
590              
591 0         0 my $remove_length = $end - $start + 1;
592              
593             my $new = $self->map_set(
594             sub {
595 0 0   0   0 $_ < $start ? $_
    0          
596             : $_ > $end ? $_ - $remove_length
597             : ();
598             }
599 0         0 );
600              
601 0         0 return $new;
602             }
603              
604             sub cover {
605 6     6 0 16 my $self = shift;
606              
607 6         21 my $cover = blessed($self)->new;
608 6 100       19 if ( $self->is_not_empty ) {
609 5         9 $cover->add_pair( $self->min, $self->max );
610             }
611 6         13 return $cover;
612             }
613              
614             sub holes {
615 14     14 0 26 my $self = shift;
616              
617 14         41 my $holes = blessed($self)->new;
618              
619 14 100 66     78 if ( $self->is_empty or $self->is_universal ) {
620              
621             # empty set and universal set have no holes
622             }
623             else {
624 13         15 my $c_set = $self->complement;
625 13         60 my @ranges = $c_set->ranges;
626              
627             # Remove infinite arms of complement set
628 13 50       34 if ( $c_set->is_neg_inf ) {
629              
630 13         7 shift @ranges;
631 13         12 shift @ranges;
632             }
633 13 50       98 if ( $c_set->is_pos_inf ) {
634 13         11 pop @ranges;
635 13         8 pop @ranges;
636             }
637 13         19 $holes->add_range(@ranges);
638             }
639              
640 14         22 return $holes;
641             }
642              
643             sub inset {
644 18     18 0 35 my $self = shift;
645 18         12 my $n = shift;
646              
647 18         48 my $inset = blessed($self)->new;
648 18         71 my @ranges = $self->ranges;
649 18         34 while (@ranges) {
650 38         29 my $lower = shift @ranges;
651 38         25 my $upper = shift @ranges;
652 38 100       68 if ( $lower != $self->NEG_INF ) {
653 34         23 $lower += $n;
654             }
655 38 100       62 if ( $upper != $self->POS_INF ) {
656 34         21 $upper -= $n;
657             }
658 38 100       119 $inset->add_pair( $lower, $upper )
659             if $lower <= $upper;
660             }
661              
662 18         27 return $inset;
663             }
664              
665             sub trim {
666 1     1 0 2 my $self = shift;
667 1         1 my $n = shift;
668 1         3 return $self->inset($n);
669             }
670              
671             sub pad {
672 1     1 0 1 my $self = shift;
673 1         2 my $n = shift;
674 1         2 return $self->inset( -$n );
675             }
676              
677             sub excise {
678 7     7 0 16 my $self = shift;
679 7         4 my $minlength = shift;
680              
681 7         21 my $set = blessed($self)->new;
682 7         13 map { $set->merge($_) } grep { $_->size >= $minlength } $self->sets;
  10         12  
  14         18  
683              
684 7         20 return $set;
685             }
686              
687             sub fill {
688 8     8 0 29 my $self = shift;
689 8         9 my $maxlength = shift;
690              
691 8         19 my $set = $self->copy;
692 8 50       13 if ( $maxlength > 0 ) {
693 8         11 for my $hole ( $set->holes->sets ) {
694 11 100       14 if ( $hole->size <= $maxlength ) {
695 9         12 $set->merge($hole);
696             }
697             }
698             }
699 8         24 return $set;
700             }
701              
702             sub overlap {
703 63     63 0 70 my $self = shift;
704 63         40 my $supplied = shift;
705 63         75 return $self->intersect($supplied)->size;
706             }
707              
708             sub distance {
709 28     28 0 38 my $self = shift;
710 28         22 my $supplied = shift;
711              
712 28 50 33     34 return unless $self->size and $supplied->size;
713              
714 28         37 my $overlap = $self->overlap($supplied);
715 28 100       68 return -$overlap if $overlap;
716              
717 24         24 my $min_d;
718 24         28 for my $span1 ( $self->sets ) {
719 29         36 for my $span2 ( $supplied->sets ) {
720 29         40 my $d1 = abs( $span1->min - $span2->max );
721 29         50 my $d2 = abs( $span1->max - $span2->min );
722 29 100       47 my $d = $d1 < $d2 ? $d1 : $d2;
723 29 100 100     75 if ( !defined $min_d or $d < $min_d ) {
724 27         67 $min_d = $d;
725             }
726             }
727             }
728              
729 24         44 return $min_d;
730             }
731              
732             sub find_islands {
733 11     11 0 46 my $self = shift;
734 11         7 my $supplied = shift;
735              
736 11         10 my $island;
737 11 100       22 if ( ref $supplied eq __PACKAGE__ ) {
    50          
738 4         16 $island = $self->_find_islands_set($supplied);
739             }
740             elsif ( isint($supplied) ) {
741 7         8 $island = $self->_find_islands_int($supplied);
742             }
743             else {
744 0         0 confess "Don't know how to deal with input to find_island\n";
745             }
746              
747 11         12 return $island;
748             }
749              
750             sub _find_islands_int {
751 7     7   6 my $self = shift;
752 7         6 my $number = shift;
753              
754 7         21 my $island = blessed($self)->new;
755              
756             # if $pos & 1, i.e. $pos is odd number, $val is in the set
757 7         18 my $pos = $self->_find_pos( $number + 1, 0 );
758 7 100       13 if ( $pos & 1 ) {
759 5         21 my @ranges = $self->ranges;
760 5         15 $island->add_range( $ranges[ $pos - 1 ], $ranges[$pos] );
761             }
762              
763 7         8 return $island;
764             }
765              
766             sub _find_islands_set {
767 4     4   5 my $self = shift;
768 4         4 my $supplied = shift;
769              
770 4         11 my $islands = blessed($self)->new;
771              
772 4 50       9 if ( $self->overlap($supplied) ) {
773 4         6 for my $subset ( $self->sets ) {
774 8 100       9 $islands->merge($subset) if $subset->overlap($supplied);
775             }
776             }
777              
778 4         14 return $islands;
779             }
780              
781             sub nearest_island {
782 10     10 0 46 my $self = shift;
783 10         9 my $supplied = shift;
784              
785 10 100       61 if ( ref $supplied eq __PACKAGE__ ) { # just OK
    50          
786             }
787             elsif ( isint($supplied) ) {
788 7         22 $supplied = blessed($self)->new($supplied);
789             }
790             else {
791 0         0 confess "Don't know how to deal with input to nearest_island\n";
792             }
793              
794 10         26 my $island = blessed($self)->new;
795 10         10 my $min_d;
796 10         14 for my $s ( $self->sets ) {
797 19         29 for my $ss ( $supplied->sets ) {
798 19 100       29 next if $s->overlap($ss);
799 17         36 my $d = $s->distance($ss);
800 17 100 100     47 if ( !defined $min_d or $d <= $min_d ) {
801 14 100 100     31 if ( defined $min_d and $d == $min_d ) {
802 2         3 $island->merge($s);
803             }
804             else {
805 12         6 $min_d = $d;
806 12         49 $island = $s->copy;
807             }
808             }
809             }
810             }
811              
812 10         27 return $island;
813             }
814              
815             sub at_island {
816 13     13 0 64 my $self = shift;
817 13         11 my $index = shift;
818              
819 13 100 100     68 return if $index == 0 or abs($index) > $self->span_size;
820              
821 8         11 my @islands = $self->sets;
822              
823 8 100       26 return $index < 0 ? $islands[$index] : $islands[ $index - 1 ];
824             }
825              
826             #----------------------------------------------------------#
827             # Internal methods
828             #----------------------------------------------------------#
829             # Converts a list of integers into pairs of ranges
830             sub _list_to_ranges {
831 0     0   0 my $self = shift;
832              
833 0         0 my @list = sort { $a <=> $b } @_;
  0         0  
834 0         0 my @ranges;
835 0         0 my $count = scalar @list;
836 0         0 my $pos = 0;
837 0         0 while ( $pos < $count ) {
838 0         0 my $end = $pos + 1;
839 0   0     0 $end++ while $end < $count && $list[$end] <= $list[ $end - 1 ] + 1;
840 0         0 push @ranges, ( $list[$pos], $list[ $end - 1 ] );
841 0         0 $pos = $end;
842             }
843              
844 0         0 return @ranges;
845             }
846              
847             # Converts a runlist into pairs of ranges
848             sub _runlist_to_ranges {
849 0     0   0 my $self = shift;
850              
851 0         0 my $runlist = shift;
852 0         0 $runlist =~ s/\s|_//g;
853 0 0       0 return if $runlist eq $self->EMPTY_STRING;
854              
855 0         0 my @ranges;
856              
857 0         0 for my $run ( split ",", $runlist ) {
858 0 0       0 if ( $run =~ /^ (-?\d+) $/x ) {
    0          
859 0         0 push @ranges, ( $1, $1 );
860             }
861             elsif ( $run =~ /^ (-?\d+) - (-?\d+) $/x ) {
862 0 0       0 confess "Bad order: $runlist\n" if $1 > $2;
863 0         0 push @ranges, ( $1, $2 );
864             }
865             else {
866 0         0 confess "Bad syntax: $runlist\n";
867             }
868             }
869              
870 0         0 return @ranges;
871             }
872              
873             # Converts a set specification into a set
874             sub _real_set {
875 726     726   488 my $self = shift;
876 726         453 my $supplied = shift;
877              
878 726 100 66     2429 if ( defined $supplied and ref $supplied eq __PACKAGE__ ) {
879 722         1727 return $supplied;
880             }
881             else {
882 4         19 return blessed($self)->new($supplied);
883             }
884             }
885              
886             # _find_pos
887              
888             #----------------------------------------------------------#
889             # Aliases
890             #----------------------------------------------------------#
891              
892 434     434 0 16647 sub runlist { shift->as_string; }
893 0     0 0 0 sub run_list { shift->as_string; }
894 72     72 0 302 sub elements { shift->as_array; }
895 146     146 0 354 sub size { shift->cardinality; }
896 0     0 0 0 sub count { shift->cardinality; }
897 0     0 0 0 sub empty { shift->is_empty; }
898 195     195 0 442 sub contains { shift->contains_all(@_); }
899 0     0 0 0 sub contain { shift->contains_all(@_); }
900 0     0 0 0 sub member { shift->contains_all(@_); }
901 0     0 0 0 sub duplicate { shift->copy; }
902 1     1 0 5 sub intersection { shift->intersect(@_); }
903 17     17 0 34 sub equals { shift->equal(@_); }
904 0     0 0   sub join_span { shift->fill(@_); }
905              
906             1; # Magic true value required at end of module
907              
908             __END__