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   182133 use strict;
  13         19  
  13         314  
3 13     13   41 use warnings;
  13         13  
  13         295  
4              
5 13     13   48 use base qw( DynaLoader );
  13         16  
  13         1043  
6 13     13   45 use Carp;
  13         14  
  13         762  
7 13     13   51 use Scalar::Util qw(blessed);
  13         17  
  13         905  
8 13     13   5271 use Scalar::Util::Numeric qw(isint);
  13         5630  
  13         1278  
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         97 q{""} => q{runlist},
14              
15             # use Perl standard behaviours for other operations
16             fallback => 1,
17 13     13   11704 );
  13         9358  
18              
19             BEGIN {
20 13     13   981 our $VERSION = '1.0.3';
21 13         41949 bootstrap AlignDB::IntSpanXS, $VERSION;
22             }
23              
24             # POS_INF
25             # NEG_INF
26             # EMPTY_STRING
27              
28             sub new {
29 11809     11809 0 3545782 my $class = shift;
30 11809         32170 my $self = _new($class);
31 11809 100       20170 $self->add(@_) if @_ > 0;
32 11809         16639 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 75 my $self = shift;
50 126         283 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 40 my $self = shift;
62              
63 19         13 my @spans;
64 19         75 my @ranges = $self->ranges;
65 19         42 while (@ranges) {
66 20         20 my $lower = shift @ranges;
67 20         15 my $upper = shift @ranges;
68 20         49 push @spans, [ $lower, $upper ];
69             }
70              
71 19         32 return @spans;
72             }
73              
74             sub sets {
75 128     128 0 132 my $self = shift;
76              
77 128         89 my @sets;
78 128         297 my @ranges = $self->ranges;
79 128         183 while (@ranges) {
80 163         120 my $lower = shift @ranges;
81 163         97 my $upper = shift @ranges;
82 163         458 push @sets, blessed($self)->new("$lower-$upper");
83             }
84              
85 128         184 return @sets;
86             }
87              
88             sub runlists {
89 19     19 0 42 my $self = shift;
90              
91 19 100       46 if ( $self->is_empty ) {
92 2         7 return $self->EMPTY_STRING;
93             }
94              
95 17         14 my @runlists;
96 17         50 my @ranges = $self->ranges;
97 17         27 while (@ranges) {
98 20         31 my $lower = shift @ranges;
99 20         10 my $upper = shift @ranges;
100 20 100       38 my $string = $lower == $upper ? $lower : $lower . '-' . $upper;
101 20         41 push @runlists, $string;
102             }
103              
104 17         33 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 196     196 0 123 my $self = shift;
118              
119 196         140 for my $i (@_) {
120 196 100       386 return 0 unless $self->_contains($i);
121             }
122              
123 100         94 return 1;
124             }
125              
126             sub contains_any {
127 3     3 0 5 my $self = shift;
128              
129 3         4 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 657147 my $self = shift;
143 30444         30955 my @ranges = @_;
144              
145 30444 50       43942 if ( scalar(@ranges) % 2 == 1 ) {
146 0         0 confess "Number of ranges must be even: @ranges\n";
147             }
148              
149 30444         37149 while (@ranges) {
150 37958         25493 my $from = shift @ranges;
151 37958         24237 my $to = shift @ranges;
152 37958         81348 $self->add_pair( $from, $to );
153             }
154              
155 30444         28878 return $self;
156             }
157              
158             sub add {
159 8099     8099 0 12420 my $self = shift;
160 8099         5018 my $first = shift;
161              
162 8099 100       11859 if ( ref $first eq __PACKAGE__ ) {
    100          
163 6447         13604 $self->add_range( $first->ranges );
164             }
165             elsif ( isint($first) ) {
166 806 100       989 if ( scalar @_ > 0 ) {
167 38         238 $self->add_array( [ $first, @_ ] );
168             }
169             else {
170 768         1439 $self->add_int($first);
171             }
172             }
173             else {
174 846         3725 $self->add_runlist($first);
175             }
176              
177 8099         7256 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 648989 my $self = shift;
188              
189 2455         5762 $self->invert;
190 2455         3483 $self->add_range(@_);
191 2455         3853 $self->invert;
192              
193 2455         2638 return $self;
194             }
195              
196             sub remove {
197 861     861 0 1937 my $self = shift;
198 861         590 my $first = shift;
199              
200 861 50       1566 if ( ref $first eq __PACKAGE__ ) {
    0          
201 861         3623 $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         2315 return $self;
216             }
217              
218             sub merge {
219 171     171 0 883 my $self = shift;
220              
221 171         170 foreach my $supplied (@_) {
222 177         182 my @ranges = $self->_real_set($supplied)->ranges;
223 177         250 $self->add_range(@ranges);
224             }
225              
226 171         289 return $self;
227             }
228              
229             sub subtract {
230 167     167 0 113 my $self = shift;
231              
232 167         185 foreach my $supplied (@_) {
233 167         197 my @ranges = $self->_real_set($supplied)->ranges;
234 167         256 $self->remove_range(@ranges);
235             }
236              
237 167         117 return $self;
238             }
239              
240             # copy
241              
242             sub union {
243 33     33 0 2457 my $self = shift;
244              
245 33         97 my $new = $self->copy;
246 33         51 $new->merge(@_);
247              
248 33         42 return $new;
249             }
250              
251             sub complement {
252 231     231 0 148 my $self = shift;
253              
254 231         497 my $new = $self->copy;
255 231         294 $new->invert;
256              
257 231         204 return $new;
258             }
259              
260             sub diff {
261 167     167 0 148 my $self = shift;
262              
263 167         374 my $new = $self->copy;
264 167         218 $new->subtract(@_);
265              
266 167         391 return $new;
267             }
268              
269             sub intersect {
270 102     102 0 99 my $self = shift;
271              
272 102         119 my $new = $self->complement;
273 102         110 for my $supplied (@_) {
274 106         125 my $temp_set = $self->_real_set($supplied)->complement;
275 106         125 $new->merge($temp_set);
276             }
277 102         135 $new->invert;
278              
279 102         132 return $new;
280             }
281              
282             sub xor {
283 10     10 0 30 return intersect( union(@_), intersect(@_)->complement );
284             }
285              
286             sub equal {
287 115     115 0 402 my $self = shift;
288              
289 115         164 for (@_) {
290 128         172 my $supplied = $self->_real_set($_);
291              
292 128 100       674 if ( $self->edge_size != $supplied->edge_size ) {
293 40         79 return 0;
294             }
295              
296 88         186 my @edges_a = $self->edges;
297 88         155 my @edges_b = $supplied->edges;
298              
299 88         211 for ( my $i = 0; $i < $self->edge_size; $i++ ) {
300 268 100       631 if ( $edges_a[$i] != $edges_b[$i] ) {
301 18         39 return 0;
302             }
303             }
304             }
305              
306 57         138 return 1;
307             }
308              
309             sub subset {
310 74     74 0 93 my $self = shift;
311 74         112 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 261 my $self = shift;
318 74         108 my $supplied = $self->_real_set(shift);
319              
320 74         106 return $supplied->diff($self)->is_empty;
321             }
322              
323             sub smaller_than {
324 36     36 0 74 my $self = shift;
325 36         23 my $supplied = shift;
326              
327 36   100     46 my $result = $self->subset($supplied) && !$self->equal($supplied);
328              
329 36 100       104 return $result ? 1 : 0;
330             }
331              
332             sub larger_than {
333 36     36 0 72 my $self = shift;
334 36         27 my $supplied = shift;
335              
336 36   100     44 my $result = $self->superset($supplied) && !$self->equal($supplied);
337              
338 36 100       105 return $result ? 1 : 0;
339             }
340              
341             sub at {
342 21     21 0 42 my $self = shift;
343 21         14 my $index = shift;
344 21 100 66     101 if ( $index == 0 || abs($index) > $self->cardinality ) {
345 8         24 return;
346             }
347 13 100       23 my $member
348             = $index < 0 ? $self->_at_neg( -$index ) : $self->_at_pos($index);
349 13         33 return $member;
350             }
351              
352             sub _at_pos {
353 6     6   6 my $self = shift;
354 6         4 my $index = shift;
355              
356 6         4 my $member;
357 6         4 my $element_before = 0;
358              
359 6         24 my @ranges = $self->ranges;
360 6         9 while (@ranges) {
361 9         9 my $lower = shift @ranges;
362 9         7 my $upper = shift @ranges;
363 9         6 my $span_size = $upper - $lower + 1;
364              
365 9 100       21 if ( $index > $element_before + $span_size ) {
366 3         4 $element_before += $span_size;
367             }
368             else {
369 6         4 $member = $index - $element_before - 1 + $lower;
370 6         6 last;
371             }
372             }
373              
374 6         6 return $member;
375             }
376              
377             sub _at_neg {
378 7     7   6 my $self = shift;
379 7         5 my $index = shift;
380              
381 7         5 my $member;
382 7         6 my $element_after = 0;
383              
384 7         22 my @r_ranges = reverse $self->ranges;
385 7         11 while (@r_ranges) {
386 12         17 my $upper = shift @r_ranges;
387 12         9 my $lower = shift @r_ranges;
388 12         9 my $span_size = $upper - $lower + 1;
389              
390 12 100       13 if ( $index > $element_after + $span_size ) {
391 5         8 $element_after += $span_size;
392             }
393             else {
394 7         8 $member = $upper - ( $index - $element_after ) + 1;
395 7         6 last;
396             }
397             }
398              
399 7         8 return $member;
400             }
401              
402             sub index {
403 11     11 0 22 my $self = shift;
404 11         8 my $member = shift;
405              
406 11         6 my $index;
407 11         8 my $element_before = 0;
408              
409 11         32 my @ranges = $self->ranges;
410 11         19 while (@ranges) {
411 16         11 my $lower = shift @ranges;
412 16         13 my $upper = shift @ranges;
413 16         11 my $span_size = $upper - $lower + 1;
414              
415 16 100 100     45 if ( $member >= $lower and $member <= $upper ) {
416 8         8 $index = $member - $lower + 1 + $element_before;
417 8         8 last;
418             }
419             else {
420 8         11 $element_before += $span_size;
421             }
422             }
423              
424 11         32 return $index;
425             }
426              
427             sub slice {
428 11     11 0 23 my $self = shift;
429 11         5 my $from = shift;
430 11         10 my $to = shift;
431              
432 11 100       15 if ( $from < 1 ) {
433 2         222 carp "Start index less than 1\n";
434 2         98 $from = 1;
435             }
436 11         22 my $slice = $self->_splice( $from, $to - $from + 1 );
437              
438 11         43 return $slice;
439             }
440              
441             sub _splice {
442 56     56   100 my $self = shift;
443 56         35 my $offset = shift;
444 56         36 my $length = shift;
445              
446 56         128 my @edges = $self->edges;
447 56         165 my $slice = blessed($self)->new;
448              
449 56         97 while ( @edges > 1 ) {
450 75         74 my ( $lower, $upper ) = @edges[ 0, 1 ];
451 75         55 my $span_size = $upper - $lower;
452              
453 75 100       71 if ( $offset <= $span_size ) {
454 45         50 last;
455             }
456             else {
457 30         26 splice( @edges, 0, 2 );
458 30         42 $offset -= $span_size;
459             }
460             }
461              
462             @edges
463 56 100       98 or return $slice; # empty set
464              
465 45         42 $edges[0] += $offset - 1;
466              
467 45         55 my @slices = $self->_splice_length( \@edges, $length );
468 45         68 while (@slices) {
469 67         49 my $lower = shift @slices;
470 67         43 my $upper = shift(@slices) - 1;
471 67         148 $slice->add_pair( $lower, $upper );
472             }
473              
474 45         201 return $slice;
475             }
476              
477             sub _splice_length {
478 45     45   32 my $self = shift;
479 45         28 my $edges_ref = shift;
480 45         31 my $length = shift;
481              
482 45 100       59 if ( !defined $length ) {
483 8         4 return @{$edges_ref}; # everything
  8         14  
484             }
485              
486 37 100       47 if ( $length <= 0 ) {
487 2         3 return (); # empty
488             }
489              
490 35         21 my @slices;
491              
492 35         51 while ( @$edges_ref > 1 ) {
493 54         39 my ( $lower, $upper ) = @$edges_ref[ 0, 1 ];
494 54         41 my $span_size = $upper - $lower;
495              
496 54 100       58 if ( $length <= $span_size ) {
497 28         15 last;
498             }
499             else {
500 26         24 push @slices, splice( @$edges_ref, 0, 2 );
501 26         41 $length -= $span_size;
502             }
503             }
504              
505 35 100       56 if (@$edges_ref) {
506 28         18 my $lower = shift @$edges_ref;
507 28         31 push @slices, $lower, $lower + $length;
508             }
509              
510 35         53 return @slices;
511             }
512              
513             sub min {
514 63     63 0 44 my $self = shift;
515              
516 63 50       109 if ( $self->is_empty ) {
517 0         0 return;
518             }
519             else {
520 63         64 return $self->edges_ref->[0];
521             }
522             }
523              
524             sub max {
525 63     63 0 46 my $self = shift;
526              
527 63 50       100 if ( $self->is_empty ) {
528 0         0 return;
529             }
530             else {
531 63         61 return $self->edges_ref->[-1] - 1;
532             }
533             }
534              
535             sub grep_set {
536 30     30 0 132 my $self = shift;
537 30         22 my $code_ref = shift;
538              
539 30         21 my @sub_elements;
540 30         57 for ( $self->elements ) {
541 150 100       1937 if ( $code_ref->() ) {
542 60         1661 push @sub_elements, $_;
543             }
544              
545             }
546 30         446 my $sub_set = blessed($self)->new(@sub_elements);
547              
548 30         45 return $sub_set;
549             }
550              
551             sub map_set {
552 42     42 0 188 my $self = shift;
553 42         31 my $code_ref = shift;
554              
555 42         27 my @map_elements;
556 42         54 for ( $self->elements ) {
557 210         478 foreach my $element ( $code_ref->() ) {
558 210 50       4566 if ( defined $element ) {
559 210         353 push @map_elements, $element;
560             }
561             }
562              
563             }
564 42         226 my $map_set = blessed($self)->new(@map_elements);
565              
566 42         59 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 18 my $self = shift;
606              
607 6         21 my $cover = blessed($self)->new;
608 6 100       21 if ( $self->is_not_empty ) {
609 5         6 $cover->add_pair( $self->min, $self->max );
610             }
611 6         13 return $cover;
612             }
613              
614             sub holes {
615 14     14 0 21 my $self = shift;
616              
617 14         52 my $holes = blessed($self)->new;
618              
619 14 100 66     76 if ( $self->is_empty or $self->is_universal ) {
620              
621             # empty set and universal set have no holes
622             }
623             else {
624 13         20 my $c_set = $self->complement;
625 13         72 my @ranges = $c_set->ranges;
626              
627             # Remove infinite arms of complement set
628 13 50       32 if ( $c_set->is_neg_inf ) {
629              
630 13         12 shift @ranges;
631 13         8 shift @ranges;
632             }
633 13 50       27 if ( $c_set->is_pos_inf ) {
634 13         9 pop @ranges;
635 13         11 pop @ranges;
636             }
637 13         17 $holes->add_range(@ranges);
638             }
639              
640 14         21 return $holes;
641             }
642              
643             sub inset {
644 18     18 0 35 my $self = shift;
645 18         17 my $n = shift;
646              
647 18         49 my $inset = blessed($self)->new;
648 18         69 my @ranges = $self->ranges;
649 18         27 while (@ranges) {
650 38         26 my $lower = shift @ranges;
651 38         29 my $upper = shift @ranges;
652 38 100       65 if ( $lower != $self->NEG_INF ) {
653 34         25 $lower += $n;
654             }
655 38 100       64 if ( $upper != $self->POS_INF ) {
656 34         23 $upper -= $n;
657             }
658 38 100       91 $inset->add_pair( $lower, $upper )
659             if $lower <= $upper;
660             }
661              
662 18         23 return $inset;
663             }
664              
665             sub trim {
666 1     1 0 2 my $self = shift;
667 1         1 my $n = shift;
668 1         1 return $self->inset($n);
669             }
670              
671             sub pad {
672 1     1 0 2 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 15 my $self = shift;
679 7         5 my $minlength = shift;
680              
681 7         19 my $set = blessed($self)->new;
682 7         8 map { $set->merge($_) } grep { $_->size >= $minlength } $self->sets;
  10         13  
  14         15  
683              
684 7         20 return $set;
685             }
686              
687             sub fill {
688 8     8 0 18 my $self = shift;
689 8         6 my $maxlength = shift;
690              
691 8         19 my $set = $self->copy;
692 8 50       13 if ( $maxlength > 0 ) {
693 8         12 for my $hole ( $set->holes->sets ) {
694 11 100       14 if ( $hole->size <= $maxlength ) {
695 9         10 $set->merge($hole);
696             }
697             }
698             }
699 8         23 return $set;
700             }
701              
702             sub overlap {
703 63     63 0 54 my $self = shift;
704 63         38 my $supplied = shift;
705 63         71 return $self->intersect($supplied)->size;
706             }
707              
708             sub distance {
709 28     28 0 37 my $self = shift;
710 28         19 my $supplied = shift;
711              
712 28 50 33     30 return unless $self->size and $supplied->size;
713              
714 28         50 my $overlap = $self->overlap($supplied);
715 28 100       59 return -$overlap if $overlap;
716              
717 24         19 my $min_d;
718 24         28 for my $span1 ( $self->sets ) {
719 29         35 for my $span2 ( $supplied->sets ) {
720 29         39 my $d1 = abs( $span1->min - $span2->max );
721 29         43 my $d2 = abs( $span1->max - $span2->min );
722 29 100       48 my $d = $d1 < $d2 ? $d1 : $d2;
723 29 100 100     71 if ( !defined $min_d or $d < $min_d ) {
724 27         60 $min_d = $d;
725             }
726             }
727             }
728              
729 24         44 return $min_d;
730             }
731              
732             sub find_islands {
733 11     11 0 43 my $self = shift;
734 11         8 my $supplied = shift;
735              
736 11         9 my $island;
737 11 100       23 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         13 return $island;
748             }
749              
750             sub _find_islands_int {
751 7     7   5 my $self = shift;
752 7         5 my $number = shift;
753              
754 7         23 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       11 if ( $pos & 1 ) {
759 5         23 my @ranges = $self->ranges;
760 5         13 $island->add_range( $ranges[ $pos - 1 ], $ranges[$pos] );
761             }
762              
763 7         6 return $island;
764             }
765              
766             sub _find_islands_set {
767 4     4   2 my $self = shift;
768 4         4 my $supplied = shift;
769              
770 4         10 my $islands = blessed($self)->new;
771              
772 4 50       6 if ( $self->overlap($supplied) ) {
773 4         5 for my $subset ( $self->sets ) {
774 8 100       9 $islands->merge($subset) if $subset->overlap($supplied);
775             }
776             }
777              
778 4         13 return $islands;
779             }
780              
781             sub nearest_island {
782 10     10 0 43 my $self = shift;
783 10         4 my $supplied = shift;
784              
785 10 100       25 if ( ref $supplied eq __PACKAGE__ ) { # just OK
    50          
786             }
787             elsif ( isint($supplied) ) {
788 7         24 $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         21 my $island = blessed($self)->new;
795 10         10 my $min_d;
796 10         13 for my $s ( $self->sets ) {
797 19         20 for my $ss ( $supplied->sets ) {
798 19 100       21 next if $s->overlap($ss);
799 17         32 my $d = $s->distance($ss);
800 17 100 100     45 if ( !defined $min_d or $d <= $min_d ) {
801 14 100 100     29 if ( defined $min_d and $d == $min_d ) {
802 2         3 $island->merge($s);
803             }
804             else {
805 12         8 $min_d = $d;
806 12         43 $island = $s->copy;
807             }
808             }
809             }
810             }
811              
812 10         28 return $island;
813             }
814              
815             sub at_island {
816 13     13 0 43 my $self = shift;
817 13         9 my $index = shift;
818              
819 13 100 100     67 return if $index == 0 or abs($index) > $self->span_size;
820              
821 8         11 my @islands = $self->sets;
822              
823 8 100       24 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   493 my $self = shift;
876 726         473 my $supplied = shift;
877              
878 726 100 66     2152 if ( defined $supplied and ref $supplied eq __PACKAGE__ ) {
879 722         1713 return $supplied;
880             }
881             else {
882 4         18 return blessed($self)->new($supplied);
883             }
884             }
885              
886             # _find_pos
887              
888             #----------------------------------------------------------#
889             # Aliases
890             #----------------------------------------------------------#
891              
892 434     434 0 24622 sub runlist { shift->as_string; }
893 0     0 0 0 sub run_list { shift->as_string; }
894 72     72 0 274 sub elements { shift->as_array; }
895 146     146 0 375 sub size { shift->cardinality; }
896 0     0 0 0 sub count { shift->cardinality; }
897 0     0 0 0 sub empty { shift->is_empty; }
898 196     196 0 444 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 31 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__