| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | # Number::Range::Regex::CompoundRange | 
| 2 |  |  |  |  |  |  | # | 
| 3 |  |  |  |  |  |  | # Copyright 2012 Brian Szymanski.  All rights reserved.  This module is | 
| 4 |  |  |  |  |  |  | # free software; you can redistribute it and/or modify it under the same | 
| 5 |  |  |  |  |  |  | # terms as Perl itself. | 
| 6 |  |  |  |  |  |  |  | 
| 7 |  |  |  |  |  |  | package Number::Range::Regex::CompoundRange; | 
| 8 |  |  |  |  |  |  |  | 
| 9 | 14 |  |  | 14 |  | 80 | use strict; | 
|  | 14 |  |  |  |  | 24 |  | 
|  | 14 |  |  |  |  | 583 |  | 
| 10 |  |  |  |  |  |  |  | 
| 11 | 14 |  |  | 14 |  | 71 | use vars qw ( @ISA @EXPORT @EXPORT_OK $VERSION ); | 
|  | 14 |  |  |  |  | 31 |  | 
|  | 14 |  |  |  |  | 1926 |  | 
| 12 |  |  |  |  |  |  | eval { require warnings; }; #it's ok if we can't load warnings | 
| 13 |  |  |  |  |  |  |  | 
| 14 |  |  |  |  |  |  | require Exporter; | 
| 15 | 14 |  |  | 14 |  | 72 | use base 'Exporter'; | 
|  | 14 |  |  |  |  | 24 |  | 
|  | 14 |  |  |  |  | 1691 |  | 
| 16 |  |  |  |  |  |  | @ISA    = qw( Exporter Number::Range::Regex::Range ); | 
| 17 |  |  |  |  |  |  |  | 
| 18 |  |  |  |  |  |  | $VERSION = '0.32'; | 
| 19 |  |  |  |  |  |  |  | 
| 20 | 14 |  |  | 14 |  | 8929 | use Number::Range::Regex::Util; | 
|  | 14 |  |  |  |  | 51 |  | 
|  | 14 |  |  |  |  | 4531 |  | 
| 21 | 14 |  |  | 14 |  | 8803 | use Number::Range::Regex::Util::inf qw ( neg_inf pos_inf ); | 
|  | 14 |  |  |  |  | 38 |  | 
|  | 14 |  |  |  |  | 46783 |  | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | sub new { | 
| 24 | 918 | 50 |  | 918 | 0 | 10955 | my $opts = option_mangler( ref $_[-1] eq 'HASH' ? pop : undef ); | 
| 25 | 918 |  |  |  |  | 2205 | my ($class, @ranges) = @_; | 
| 26 |  |  |  |  |  |  | # TODO: do we need to collapse&sort the ranges? either by calling | 
| 27 |  |  |  |  |  |  | # multi_union (which has a collapsing effect) or by an explicit sort | 
| 28 |  |  |  |  |  |  | # by min + verify no overlaps + _collapse_ranges ? | 
| 29 | 918 |  |  |  |  | 8729 | return bless { ranges => [ @ranges ], opts => $opts }, $class; | 
| 30 |  |  |  |  |  |  | } | 
| 31 |  |  |  |  |  |  |  | 
| 32 |  |  |  |  |  |  | sub to_string { | 
| 33 | 403 |  |  | 403 | 0 | 21933 | my ($self, $passed_opts) = @_; | 
| 34 | 403 |  |  |  |  | 1959 | return join(',', map { $_->to_string() } @{$self->{ranges}}); | 
|  | 868 |  |  |  |  | 5248 |  | 
|  | 403 |  |  |  |  | 1202 |  | 
| 35 |  |  |  |  |  |  | } | 
| 36 |  |  |  |  |  |  |  | 
| 37 |  |  |  |  |  |  | sub regex { | 
| 38 | 216 |  |  | 216 | 0 | 13470 | my ($self, $passed_opts) = @_; | 
| 39 |  |  |  |  |  |  |  | 
| 40 | 216 |  |  |  |  | 1010 | my $opts = option_mangler( $self->{opts}, $passed_opts ); | 
| 41 |  |  |  |  |  |  |  | 
| 42 | 216 | 50 |  |  |  | 769 | my $separator = $opts->{readable} ? ' | ' : '|'; | 
| 43 | 216 |  |  |  |  | 367 | my $regex_str; | 
| 44 | 216 | 100 |  |  |  | 337 | if(@{$self->{ranges}}) { | 
|  | 216 |  |  |  |  | 622 |  | 
| 45 | 197 |  |  |  |  | 321 | $regex_str = join $separator, map { $_->regex( { %$opts, comment => 0 } ) } @{$self->{ranges}}; | 
|  | 437 |  |  |  |  | 4971 |  | 
|  | 197 |  |  |  |  | 398 |  | 
| 46 |  |  |  |  |  |  | } else { | 
| 47 | 19 |  |  |  |  | 33 | $regex_str = '(?!)'; # never matches | 
| 48 |  |  |  |  |  |  | } | 
| 49 | 216 | 50 |  |  |  | 1026 | $regex_str = " $regex_str " if $opts->{readable}; | 
| 50 |  |  |  |  |  |  |  | 
| 51 | 216 | 50 |  |  |  | 539 | my $modifier_maybe = $opts->{readable} ? '(?x)' : ''; | 
| 52 | 216 |  |  |  |  | 422 | my ($begin_comment_maybe, $end_comment_maybe) = ('', ''); | 
| 53 | 216 | 50 |  |  |  | 546 | if($opts->{comment}) { | 
| 54 | 216 |  |  |  |  | 620 | my $comment = "Number::Range::Regex::CompoundRange[".$self->to_string."]"; | 
| 55 | 216 | 50 |  |  |  | 1032 | $begin_comment_maybe = $opts->{readable} ? " # begin $comment" : "(?# begin $comment )"; | 
| 56 | 216 | 50 |  |  |  | 923 | $end_comment_maybe = $opts->{readable} ? " # end $comment" : "(?# end $comment )"; | 
| 57 |  |  |  |  |  |  | } | 
| 58 | 216 |  |  |  |  | 5415 | return qr/(?:$begin_comment_maybe$modifier_maybe(?:$regex_str)$end_comment_maybe)/; | 
| 59 |  |  |  |  |  |  | } | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | sub _do_unequal_min { | 
| 62 |  |  |  |  |  |  | #warn "in _do_unequal_min"; | 
| 63 | 513 |  |  | 513 |  | 796 | my ($self, $lower, $upper, $ptr, $ranges) = @_; | 
| 64 | 513 | 100 |  |  |  | 1702 | if( $lower->{max} > $upper->{max} ) { | 
|  |  | 100 |  |  |  |  |  | 
| 65 |  |  |  |  |  |  | # 3 ranges, last of which may yet overlap | 
| 66 | 47 |  |  |  |  | 224 | my $r1 = Number::Range::Regex::SimpleRange->new( $lower->{min}, $upper->{min}-1 ); | 
| 67 | 47 |  |  |  |  | 89 | my $r2 = $upper; | 
| 68 | 47 |  |  |  |  | 235 | my $r3 = Number::Range::Regex::SimpleRange->new( $upper->{max}+1, $lower->{max} ); | 
| 69 |  |  |  |  |  |  | #warn "l: $lower->{min}..$lower->{max} -> $r1->{min}..$r1->{max},$r2->{min}..$r2->{max},$r3->{min}..$r3->{max}"; | 
| 70 | 47 |  |  |  |  | 145 | splice( @$ranges, $$ptr, 1, ($r1, $r2, $r3) ); | 
| 71 | 47 |  |  |  |  | 392 | $$ptr += 2; # $r3 may overlap something else | 
| 72 |  |  |  |  |  |  | } elsif( $lower->{max} >= $upper->{min} ) { | 
| 73 |  |  |  |  |  |  | # 2 ranges, latter of which may yet overlap | 
| 74 | 32 |  |  |  |  | 177 | my $r1 = Number::Range::Regex::SimpleRange->new( $lower->{min}, $upper->{min}-1 ); | 
| 75 | 32 |  |  |  |  | 180 | my $r2 = Number::Range::Regex::SimpleRange->new( $upper->{min}, $lower->{max} ); | 
| 76 |  |  |  |  |  |  | #warn "l: $lower->{min}..$lower->{max} -> $r1->{min}..$r1->{max},$r2->{min}..$r2->{max}"; | 
| 77 | 32 |  |  |  |  | 104 | splice( @$ranges, $$ptr, 1, ($r1, $r2 ) ); | 
| 78 | 32 |  |  |  |  | 302 | $$ptr += 1; | 
| 79 |  |  |  |  |  |  | } else { # $lower->{max} < $upper->{min} | 
| 80 |  |  |  |  |  |  | # 1 range, no overlap | 
| 81 |  |  |  |  |  |  | #warn "l: $lower->{min}..$lower->{max} is ok"; | 
| 82 | 434 |  |  |  |  | 2563 | $$ptr++; | 
| 83 |  |  |  |  |  |  | } | 
| 84 |  |  |  |  |  |  | } | 
| 85 |  |  |  |  |  |  |  | 
| 86 |  |  |  |  |  |  | sub sectionify { | 
| 87 | 725 |  |  | 725 | 0 | 1033 | my ($self, $other) = @_; | 
| 88 |  |  |  |  |  |  |  | 
| 89 | 725 |  |  |  |  | 815 | my @s_ranges = @{$self->{ranges}}; | 
|  | 725 |  |  |  |  | 6850 |  | 
| 90 | 725 | 50 |  |  |  | 7753 | my @o_ranges = $other->isa('Number::Range::Regex::CompoundRange') ? @{$other->{ranges}} : | 
|  | 44 | 100 |  |  |  | 256 |  | 
| 91 |  |  |  |  |  |  | $other->isa('Number::Range::Regex::SimpleRange') ? ( $other ) : | 
| 92 |  |  |  |  |  |  | die "other is neither a simple nor compound range!"; | 
| 93 |  |  |  |  |  |  |  | 
| 94 |  |  |  |  |  |  | #warn "s_ranges1: ".join ",", map { "$_->{min}..$_->{max}" } @s_ranges; | 
| 95 |  |  |  |  |  |  | #warn "o_ranges1: ".join ",", map { "$_->{min}..$_->{max}" } @o_ranges; | 
| 96 |  |  |  |  |  |  |  | 
| 97 |  |  |  |  |  |  | # munge ranges so that there are no partial overlaps - only | 
| 98 |  |  |  |  |  |  | # non-overlaps and complete overlaps e.g: | 
| 99 |  |  |  |  |  |  | #   if s=(6..12) and o=(7..13): | 
| 100 |  |  |  |  |  |  | #      s=(6,7..12) and o=(7..12,13); | 
| 101 |  |  |  |  |  |  | #   if s=(6..12) and o=(7..9): | 
| 102 |  |  |  |  |  |  | #      s=(6,7..9,10..12) and o=(7..9); | 
| 103 | 725 |  |  |  |  | 1598 | my ($s_ptr, $o_ptr) = (0, 0); | 
| 104 | 725 |  | 100 |  |  | 3087 | while( ($s_ptr < @s_ranges) && ($o_ptr < @o_ranges) ) { | 
| 105 |  |  |  |  |  |  | #warn "s_ranges: @s_ranges, o_ranges: @o_ranges"; | 
| 106 | 585 |  |  |  |  | 856 | my $this_s = $s_ranges[$s_ptr]; | 
| 107 | 585 |  |  |  |  | 1469 | my $this_o = $o_ranges[$o_ptr]; | 
| 108 |  |  |  |  |  |  | #warn "checking this_s: $this_s->{min}..$this_s->{max}, this_o: $this_o->{min}..$this_o->{max}"; | 
| 109 | 585 | 100 |  |  |  | 1751 | if( $this_s->{min} < $this_o->{min} ) { | 
|  |  | 100 |  |  |  |  |  | 
| 110 |  |  |  |  |  |  | #printf STDERR "l==s, "; | 
| 111 | 368 |  |  |  |  | 963 | $self->_do_unequal_min($this_s, $this_o, \$s_ptr, \@s_ranges ); | 
| 112 |  |  |  |  |  |  | } elsif( $this_s->{min} > $this_o->{min} ) { | 
| 113 |  |  |  |  |  |  | #printf STDERR "l==o, "; | 
| 114 | 145 |  |  |  |  | 415 | $self->_do_unequal_min($this_o, $this_s, \$o_ptr, \@o_ranges ); | 
| 115 |  |  |  |  |  |  | } else { # $this_s->{min} == $this_o->{min} | 
| 116 | 72 | 100 |  |  |  | 307 | if( $this_s->{max} < $this_o->{max} ) { | 
|  |  | 100 |  |  |  |  |  | 
| 117 |  |  |  |  |  |  | # 2 ranges, latter of which may yet overlap | 
| 118 | 13 |  |  |  |  | 35 | my $r1 = $this_s; | 
| 119 | 13 |  |  |  |  | 72 | my $r2 = Number::Range::Regex::SimpleRange->new($this_s->{max}+1, $this_o->{max} ); | 
| 120 | 13 |  |  |  |  | 41 | splice( @o_ranges, $o_ptr, 1, ($r1, $r2) ); | 
| 121 |  |  |  |  |  |  | #warn "o: $this_o->{min}..$this_o->{max} -> $r1->{min}..$r1->{max},$r2->{min}..$r2->{max}"; | 
| 122 | 13 |  |  |  |  | 78 | $o_ptr++; # $r2 may overlap something else | 
| 123 |  |  |  |  |  |  | } elsif( $this_s->{max} > $this_o->{max} ) { | 
| 124 |  |  |  |  |  |  | # 2 ranges, latter of which may yet overlap | 
| 125 | 19 |  |  |  |  | 42 | my $r1 = $this_o; | 
| 126 | 19 |  |  |  |  | 117 | my $r2 = Number::Range::Regex::SimpleRange->new($this_o->{max}+1, $this_s->{max} ); | 
| 127 | 19 |  |  |  |  | 71 | splice( @s_ranges, $s_ptr, 1, ($r1, $r2) ); | 
| 128 |  |  |  |  |  |  | #warn "s: $this_s->{min}..$this_s->{max} -> $r1->{min}..$r1->{max},$r2->{min}..$r2->{max}"; | 
| 129 | 19 |  |  |  |  | 133 | $s_ptr++; # $r2 may overlap something else | 
| 130 |  |  |  |  |  |  | } else { # $this_s->{max} == $this_o->{min} | 
| 131 |  |  |  |  |  |  | # 1 range, no overlap | 
| 132 |  |  |  |  |  |  | #warn "s/o: $this_o->{min}..$this_o->{max} is ok"; | 
| 133 | 40 |  |  |  |  | 64 | $s_ptr++; | 
| 134 | 40 |  |  |  |  | 170 | $o_ptr++; | 
| 135 |  |  |  |  |  |  | } | 
| 136 |  |  |  |  |  |  | } | 
| 137 |  |  |  |  |  |  | } | 
| 138 |  |  |  |  |  |  |  | 
| 139 |  |  |  |  |  |  | #warn "s_ranges2: ".join ",", map { "$_->{min}..$_->{max}" } @s_ranges; | 
| 140 |  |  |  |  |  |  | #warn "o_ranges2: ".join ",", map { "$_->{min}..$_->{max}" } @o_ranges; | 
| 141 |  |  |  |  |  |  |  | 
| 142 | 725 |  |  |  |  | 951 | my $sections; | 
| 143 | 725 |  |  |  |  | 1210 | ($s_ptr, $o_ptr) = (0, 0); | 
| 144 | 725 |  | 100 |  |  | 2825 | while( ($s_ptr < @s_ranges) && ($o_ptr < @o_ranges) ) { | 
| 145 | 553 |  |  |  |  | 4817 | my $this_s = $s_ranges[$s_ptr]; | 
| 146 | 553 |  |  |  |  | 786 | my $this_o = $o_ranges[$o_ptr]; | 
| 147 | 553 | 100 |  |  |  | 1600 | if( $this_s->{min} < $this_o->{min} ) { | 
|  |  | 100 |  |  |  |  |  | 
| 148 | 328 |  |  |  |  | 336 | push @{$sections->{just_self}}, $this_s; | 
|  | 328 |  |  |  |  | 891 |  | 
| 149 | 328 |  |  |  |  | 402 | push @{$sections->{in_either}}, $this_s; | 
|  | 328 |  |  |  |  | 10373 |  | 
| 150 | 328 |  |  |  |  | 1415 | $s_ptr++; | 
| 151 |  |  |  |  |  |  | } elsif( $this_o->{min} < $this_s->{min} ) { | 
| 152 | 106 |  |  |  |  | 129 | push @{$sections->{just_other}}, $this_o; | 
|  | 106 |  |  |  |  | 256 |  | 
| 153 | 106 |  |  |  |  | 141 | push @{$sections->{in_either}}, $this_o; | 
|  | 106 |  |  |  |  | 199 |  | 
| 154 | 106 |  |  |  |  | 471 | $o_ptr++; | 
| 155 |  |  |  |  |  |  | } else { # $this_s->{min} == $this_o->{min} | 
| 156 | 119 | 50 |  |  |  | 384 | die "internal error in sectionify"  unless  $this_s->{max} == $this_o->{max}; | 
| 157 | 119 |  |  |  |  | 142 | push @{$sections->{in_both}}, $this_s; | 
|  | 119 |  |  |  |  | 307 |  | 
| 158 | 119 |  |  |  |  | 171 | push @{$sections->{in_either}}, $this_s; | 
|  | 119 |  |  |  |  | 219 |  | 
| 159 | 119 |  |  |  |  | 151 | $s_ptr++; | 
| 160 | 119 |  |  |  |  | 494 | $o_ptr++; | 
| 161 |  |  |  |  |  |  | } | 
| 162 |  |  |  |  |  |  | } | 
| 163 | 725 | 100 |  |  |  | 1793 | if( $o_ptr < @o_ranges ) { | 
|  |  | 100 |  |  |  |  |  | 
| 164 | 661 |  |  |  |  | 41197 | push @{$sections->{just_other}}, @o_ranges[$o_ptr..$#o_ranges]; | 
|  | 661 |  |  |  |  | 5509 |  | 
| 165 | 661 |  |  |  |  | 985 | push @{$sections->{in_either}},  @o_ranges[$o_ptr..$#o_ranges]; | 
|  | 661 |  |  |  |  | 2167 |  | 
| 166 |  |  |  |  |  |  | } elsif( $s_ptr < @s_ranges ) { | 
| 167 | 52 |  |  |  |  | 81 | push @{$sections->{just_self}},  @s_ranges[$s_ptr..$#s_ranges]; | 
|  | 52 |  |  |  |  | 335 |  | 
| 168 | 52 |  |  |  |  | 70 | push @{$sections->{in_either}},  @s_ranges[$s_ptr..$#s_ranges]; | 
|  | 52 |  |  |  |  | 173 |  | 
| 169 |  |  |  |  |  |  | } | 
| 170 |  |  |  |  |  |  |  | 
| 171 |  |  |  |  |  |  | #warn "just_self: ".join ",", map { "$_->{min}..$_->{max}" } @{$sections->{just_self}}; | 
| 172 |  |  |  |  |  |  | #warn "in_both: ".join ",", map { "$_->{min}..$_->{max}" } @{$sections->{in_both}}; | 
| 173 |  |  |  |  |  |  | #warn "in_either: ".join ",", map { "$_->{min}..$_->{max}" } @{$sections->{in_either}}; | 
| 174 |  |  |  |  |  |  | #warn "just_other: ".join ",", map { "$_->{min}..$_->{max}" } @{$sections->{just_other}}; | 
| 175 |  |  |  |  |  |  |  | 
| 176 | 725 |  |  |  |  | 2496 | return $sections; | 
| 177 |  |  |  |  |  |  | } | 
| 178 |  |  |  |  |  |  |  | 
| 179 |  |  |  |  |  |  |  | 
| 180 |  |  |  |  |  |  | sub intersection { | 
| 181 | 10 |  |  | 10 | 0 | 22 | my ($self, $other) = @_; | 
| 182 | 10 |  |  |  |  | 29 | my $sections = $self->sectionify( $other ); | 
| 183 | 10 |  |  |  |  | 28 | return multi_union( @{$sections->{in_both}} ); | 
|  | 10 |  |  |  |  | 50 |  | 
| 184 |  |  |  |  |  |  | } | 
| 185 |  |  |  |  |  |  |  | 
| 186 |  |  |  |  |  |  | sub subtract { | 
| 187 | 22 |  |  | 22 | 0 | 50 | my ($self, $other) = @_; | 
| 188 | 22 |  |  |  |  | 74 | my $sections = $self->sectionify( $other ); | 
| 189 | 22 |  |  |  |  | 45 | return multi_union( @{$sections->{just_self}} ); | 
|  | 22 |  |  |  |  | 103 |  | 
| 190 |  |  |  |  |  |  | } | 
| 191 |  |  |  |  |  |  |  | 
| 192 |  |  |  |  |  |  | sub xor { | 
| 193 | 12 |  |  | 12 | 0 | 31 | my ($self, $other) = @_; | 
| 194 | 12 |  |  |  |  | 34 | my $sections = $self->sectionify( $other ); | 
| 195 | 12 |  |  |  |  | 23 | return multi_union( @{$sections->{just_self}}, @{$sections->{just_other}} ); | 
|  | 12 |  |  |  |  | 180 |  | 
|  | 12 |  |  |  |  | 57 |  | 
| 196 |  |  |  |  |  |  | } | 
| 197 |  |  |  |  |  |  |  | 
| 198 |  |  |  |  |  |  | sub invert { | 
| 199 | 35 |  |  | 35 | 0 | 558 | my ($self) = @_; | 
| 200 | 35 |  |  |  |  | 72 | my @included = @{$self->{ranges}}; | 
|  | 35 |  |  |  |  | 117 |  | 
| 201 | 35 | 100 |  |  |  | 169 | return Number::Range::Regex::SimpleRange->new( neg_inf, pos_inf ) unless @included; | 
| 202 | 25 |  |  |  |  | 63 | my @excluded = (); | 
| 203 | 25 | 100 |  |  |  | 133 | if($included[0]->{min} != neg_inf ) { | 
| 204 | 22 |  |  |  |  | 63 | push @excluded, Number::Range::Regex::SimpleRange->new( neg_inf, $included[0]->{min}-1 ); | 
| 205 |  |  |  |  |  |  | } | 
| 206 | 25 |  |  |  |  | 126 | for(my $c=1; $c<@included; ++$c) { | 
| 207 | 34 |  |  |  |  | 77 | my $last = $included[$c-1]; | 
| 208 | 34 |  |  |  |  | 61 | my $this = $included[$c]; | 
| 209 | 34 | 50 |  |  |  | 135 | if($last->{max}+1 > $this->{min}-1) { | 
| 210 | 0 |  |  |  |  | 0 | die "internal error - overlapping SRs?"; | 
| 211 |  |  |  |  |  |  | } else { | 
| 212 | 34 |  |  |  |  | 213 | push @excluded, Number::Range::Regex::SimpleRange->new( $last->{max}+1, $this->{min}-1 ); | 
| 213 |  |  |  |  |  |  | } | 
| 214 |  |  |  |  |  |  | } | 
| 215 | 25 | 100 |  |  |  | 122 | if($included[-1]->{max} != pos_inf) { | 
| 216 | 22 |  |  |  |  | 83 | push @excluded, Number::Range::Regex::SimpleRange->new( $included[-1]->{max}+1, pos_inf ); | 
| 217 |  |  |  |  |  |  | } | 
| 218 | 25 |  |  |  |  | 142 | return __PACKAGE__->new( @excluded ); | 
| 219 |  |  |  |  |  |  | } | 
| 220 |  |  |  |  |  |  |  | 
| 221 |  |  |  |  |  |  | sub union { | 
| 222 | 681 | 100 |  | 681 | 0 | 3143 | my $opts = option_mangler( ref $_[-1] eq 'HASH' ? pop : undef ); | 
| 223 | 681 |  |  |  |  | 1377 | my ($self, @other) = @_; | 
| 224 |  |  |  |  |  |  | #warn "cr::u, wo: $opts->{warn_overlap}, $self, @other"; | 
| 225 | 681 | 50 |  |  |  | 1826 | return multi_union( $self, @other )  if  @other > 1; | 
| 226 | 681 |  |  |  |  | 4957 | my $sections = $self->sectionify( $other[0] ); | 
| 227 | 681 | 100 | 100 |  |  | 3609 | if( $opts->{warn_overlap} && $sections->{in_both} && @{ $sections->{in_both} } ) { | 
|  | 2 |  | 66 |  |  | 7 |  | 
| 228 | 2 | 50 |  |  |  | 5 | my $subname = $opts->{warn_overlap} eq '1' ? 'union' : $opts->{warn_overlap}; | 
| 229 | 2 |  |  |  |  | 5 | warn "$subname call got overlap(s): ", join ",", @{ $sections->{in_both} }; | 
|  | 2 |  |  |  |  | 10 |  | 
| 230 |  |  |  |  |  |  | } | 
| 231 | 681 |  |  |  |  | 936 | my @in_either = _collapse_ranges( @{$sections->{in_either}} ); | 
|  | 681 |  |  |  |  | 1957 |  | 
| 232 | 681 | 50 |  |  |  | 2445 | if( @in_either == 0 ) { | 
|  |  | 100 |  |  |  |  |  | 
| 233 | 0 |  |  |  |  | 0 | return empty_set(); | 
| 234 |  |  |  |  |  |  | } elsif( @in_either == 1 ) { | 
| 235 | 570 |  |  |  |  | 7781 | return $in_either[0]; | 
| 236 |  |  |  |  |  |  | } else { | 
| 237 | 111 |  |  |  |  | 373 | return __PACKAGE__->new( @in_either ); | 
| 238 |  |  |  |  |  |  | } | 
| 239 |  |  |  |  |  |  | } | 
| 240 |  |  |  |  |  |  |  | 
| 241 |  |  |  |  |  |  | sub _collapse_ranges { | 
| 242 | 684 |  |  | 684 |  | 1274 | my @ranges = @_; | 
| 243 | 684 |  |  |  |  | 971 | my $last_r; | 
| 244 | 684 |  |  |  |  | 937 | my $this_r = $ranges[0]; | 
| 245 | 684 |  |  |  |  | 5918 | for (my $rpos = 1; $rpos < @ranges; $rpos++ ) { | 
| 246 | 370 |  |  |  |  | 534 | $last_r = $this_r; | 
| 247 | 370 |  |  |  |  | 496 | $this_r = $ranges[$rpos]; | 
| 248 | 370 | 100 |  |  |  | 1043 | if($last_r->touches($this_r)) { | 
| 249 | 69 |  |  |  |  | 273 | $this_r = $last_r->union( $this_r ); | 
| 250 | 69 |  |  |  |  | 189 | splice(@ranges, $rpos-1, 2, $this_r); | 
| 251 | 69 |  |  |  |  | 213 | $rpos--; | 
| 252 |  |  |  |  |  |  | } | 
| 253 |  |  |  |  |  |  | } | 
| 254 | 684 |  |  |  |  | 1880 | return @ranges; | 
| 255 |  |  |  |  |  |  | } | 
| 256 |  |  |  |  |  |  |  | 
| 257 |  |  |  |  |  |  | #sub _is_contiguous { | 
| 258 |  |  |  |  |  |  | #  my ($self) = @_; | 
| 259 |  |  |  |  |  |  | #  my $last_r; | 
| 260 |  |  |  |  |  |  | #  my $this_r = $self->{ranges}->[0]; | 
| 261 |  |  |  |  |  |  | #  for (my $rpos = 1; $rpos < @{$self->{ranges}}; $rpos++ ) { | 
| 262 |  |  |  |  |  |  | #    $last_r = $this_r; | 
| 263 |  |  |  |  |  |  | #    $this_r = $self->{ranges}->[$rpos]; | 
| 264 |  |  |  |  |  |  | #    return  if  $last_r->{max}+1 < $this_r->{min}; | 
| 265 |  |  |  |  |  |  | #  } | 
| 266 |  |  |  |  |  |  | #  return ($self->{ranges}->[0]->{min}, $self->{ranges}->[-1]->{max}); | 
| 267 |  |  |  |  |  |  | #} | 
| 268 |  |  |  |  |  |  |  | 
| 269 |  |  |  |  |  |  | sub contains { | 
| 270 | 536 |  |  | 536 | 0 | 446362 | my ($self, $n) = @_; | 
| 271 | 536 |  |  |  |  | 750 | foreach my $r (@{$self->{ranges}}) { | 
|  | 536 |  |  |  |  | 1376 |  | 
| 272 | 857 | 100 |  |  |  | 2557 | return 1  if  $r->contains( $n ); | 
| 273 |  |  |  |  |  |  | } | 
| 274 | 329 |  |  |  |  | 1123 | return; | 
| 275 |  |  |  |  |  |  | } | 
| 276 |  |  |  |  |  |  |  | 
| 277 |  |  |  |  |  |  | sub is_empty { | 
| 278 | 95 |  |  | 95 | 0 | 4053 | my ($self) = @_; | 
| 279 | 95 |  |  |  |  | 123 | return !@{$self->{ranges}}; | 
|  | 95 |  |  |  |  | 534 |  | 
| 280 |  |  |  |  |  |  | } | 
| 281 |  |  |  |  |  |  |  | 
| 282 |  |  |  |  |  |  | sub has_lower_bound { | 
| 283 | 0 |  |  | 0 | 0 | 0 | my ($self) = @_; | 
| 284 | 0 | 0 |  |  |  | 0 | return  if  $self->is_empty; | 
| 285 | 0 |  |  |  |  | 0 | return $self->{ranges}->[0]->has_lower_bound; | 
| 286 |  |  |  |  |  |  | } | 
| 287 |  |  |  |  |  |  |  | 
| 288 |  |  |  |  |  |  | sub has_upper_bound { | 
| 289 | 0 |  |  | 0 | 0 | 0 | my ($self) = @_; | 
| 290 | 0 | 0 |  |  |  | 0 | return  if  $self->is_empty; | 
| 291 | 0 |  |  |  |  | 0 | return $self->{ranges}->[-1]->has_upper_bound; | 
| 292 |  |  |  |  |  |  | } | 
| 293 |  |  |  |  |  |  |  | 
| 294 |  |  |  |  |  |  | sub is_infinite { | 
| 295 | 44 |  |  | 44 | 0 | 4017 | my ($self) = @_; | 
| 296 | 44 | 100 |  |  |  | 156 | return  if  $self->is_empty; | 
| 297 | 39 |  | 100 |  |  | 203 | return ! ( $self->{ranges}->[0]->has_lower_bound && $self->{ranges}->[-1]->has_upper_bound ); | 
| 298 |  |  |  |  |  |  | } | 
| 299 |  |  |  |  |  |  |  | 
| 300 |  |  |  |  |  |  | 1; | 
| 301 |  |  |  |  |  |  |  |