File Coverage

blib/lib/RangeQuery.pm
Criterion Covered Total %
statement 42 42 100.0
branch 4 6 66.6
condition n/a
subroutine 9 9 100.0
pod 3 3 100.0
total 58 60 96.6


line stmt bran cond sub pod time code
1             package RangeQuery;
2              
3 1     1   27673 use strict;
  1         3  
  1         33  
4 1     1   6 use warnings;
  1         2  
  1         25  
5 1     1   4 use Carp;
  1         6  
  1         1304  
6              
7             our $VERSION = '0.04';
8              
9             =head1 NAME
10              
11             RangeQuery - retrieves the minimum/maximum value from a sequence within a given range.
12              
13             =head1 SYNOPSIS
14              
15             use RangeQuery qw(min_value max_value);
16              
17             my @sequence = (4,2,-8,6,-1,2);
18             my $range = RangeQuery->new(@sequence);
19             my $min = $range->min_value(0,2); # -8
20             my $max = $range->max_value (3, 5); # 6
21              
22              
23             =head1 DESCRIPTION
24              
25             Retrieves the minimum/maximum value from a sequence within a given range.
26             It takes O(n log n) to build the object and O(1) to retrieve a min/max value.
27              
28             Note: You should use a more naive approach with small sequences of values.
29              
30             =head1 METHODS
31              
32             =head2 new
33              
34             Creates a new RangeQuery object.
35             This builds the appropriate data structure in order to retrieve values efficiently.
36              
37             =cut
38              
39             sub new {
40 2     2 1 19 my ($self, @sequence) = @_;
41              
42 2 50       8 croak "Set is empty." if !$#sequence;
43              
44 2         6 my @new_sequence = @sequence;
45 2         2 my (@max, @min);
46              
47 2         6 my $obj = [\@new_sequence, \@max, \@min];
48 2         4 bless $obj, $self;
49 2         6 _build $obj;
50              
51 2         7 return $obj;
52             }
53              
54 50     50   82 sub _min { $_[$_[0] > $_[1]]; }
55 50     50   163 sub _max { $_[$_[0] < $_[1]]; }
56              
57             =head2 max_value
58              
59             Retrieves the maximum value within a given range.
60              
61             =cut
62              
63             sub max_value {
64 12     12 1 65 my ($self, $left, $right) = @_;
65 12         18 my ($sequence, $max) = ($self->[0], $self->[1]);
66              
67 12         46 my $t = log ($right - $left + 1) / log 2;
68 12         20 my $p = (1 << $t);
69              
70 12 100       94 return $max->[$left][$t] > $max->[$right - $p + 1][$t] ? $max->[$left][$t] : $max->[$right - $p + 1][$t];
71             }
72              
73             =head2 min_value
74              
75             Retrieves the minimum value within a given range.
76              
77             =cut
78              
79             sub min_value {
80 11     11 1 29 my ($self, $left, $right) = @_;
81 11         21 my ($sequence, $min) = ($self->[0], $self->[2]);
82              
83 11         40 my $t = log ($right - $left + 1) / log 2;
84 11         16 my $p = (1 << $t);
85              
86 11 50       75 return $min->[$left][$t] < $min->[$right - $p + 1][$t] ? $min->[$left][$t] : $min->[$right - $p + 1][$t];
87             }
88              
89             sub _build {
90 2     2   4 my ($self) = @_;
91 2         1 my ($sequence, $max, $min) = @{$self};
  2         8  
92 2         2 my $size = $#{$sequence};
  2         4  
93              
94 2         5 for my $i (0..$size) {
95 22         54 $min->[$i][0] = $max->[$i][0] = $sequence->[$i];
96             }
97              
98 2         9 my $s = (log $size + 1) / log 2;
99 2         6 for my $i (1 .. (log ($size + 1)) / (log 2)) {
100 6         7 my $p = (1 << ($i - 1));
101              
102 6         16 for (my $j = 0; $j + (1 << $i) - 1 <= $size; $j++) {
103 50         100 $min->[$j][$i] = _min($min->[$j][$i - 1], $min->[$j + $p][$i - 1]);
104 50         101 $max->[$j][$i] = _max($max->[$j][$i - 1], $max->[$j + $p][$i - 1]);
105             }
106             }
107             }
108              
109             1;
110             __END__