File Coverage

blib/lib/List/Range/Search/Binary.pm
Criterion Covered Total %
statement 35 35 100.0
branch 8 12 66.6
condition 2 5 40.0
subroutine 7 7 100.0
pod 2 2 100.0
total 54 61 88.5


line stmt bran cond sub pod time code
1             package List::Range::Search::Binary;
2 2     2   1094 use strict;
  2         2  
  2         54  
3 2     2   9 use warnings;
  2         2  
  2         60  
4              
5 2     2   8 use Class::Accessor::Lite ro => [qw/ranges/];
  2         2  
  2         11  
6              
7             sub new {
8 1     1 1 7 my ($class, $set, $opt) = @_;
9 1   50     5 $opt ||= {};
10              
11 1         3 my $ranges = $class->_normalize($set->ranges, $opt);
12             return bless {
13             ranges => $ranges,
14 1   33     10 max_depth => $opt->{max_depth} || int(log(@$ranges) / log(2)),
15             } => $class;
16             }
17              
18             sub _normalize {
19 1     1   6 my ($class, $ranges, $opt) = @_;
20             my $sorted = $opt->{no_sort} ? $ranges : [
21 1 50       7 sort { $a->lower <=> $b->lower || $a->upper <=> $b->upper } @$ranges
  9 50       42  
22             ];
23 1 50       9 return $opt->{no_verify} ? $sorted : $class->_verify($sorted);
24             }
25              
26             sub _verify {
27 1     1   2 my ($class, $ranges) = @_;
28 1         1 for my $i (0..$#{$ranges}-1) {
  1         4  
29 5         21 my $before = $ranges->[$i];
30 5         4 my $after = $ranges->[$i+1];
31 5 50       6 die "Binary search does not support to search in the crossed ranges"
32             if $after->lower <= $before->upper;
33             }
34 1         6 return $ranges;
35             }
36              
37             sub find {
38 7     7 1 5675 my ($self, $value) = @_;
39              
40 7         6 my @ranges = @{ $self->{ranges} };
  7         18  
41 7         18 for (0..$self->{max_depth}) {
42 18         30 my $point = int($#ranges / 2);
43 18         17 my $range = $ranges[$point];
44 18 100       32 if ($value < $range->lower) {
    100          
45 4         25 splice @ranges, $point;
46             }
47             elsif ($value > $range->upper) {
48 8         76 splice @ranges, 0, $point + 1;
49             }
50             else {
51             # includes
52 6         59 return $range;
53             }
54             }
55              
56             # not found
57 1         6 return undef; ## no critic
58             }
59              
60             1;
61             __END__