File Coverage

blib/lib/Tie/NumericRange.pm
Criterion Covered Total %
statement 88 102 86.2
branch 25 36 69.4
condition 6 12 50.0
subroutine 13 13 100.0
pod n/a
total 132 163 80.9


line stmt bran cond sub pod time code
1             #
2             # Tie::NumericRange
3             # (c) 2008 Michael Gregorowicz
4             # the mg2 organization
5             #
6              
7             package Tie::NumericRange;
8              
9 1     1   36088 use 5.006001;
  1         3  
  1         50  
10 1     1   6 use strict;
  1         2  
  1         32  
11 1     1   7 no warnings;
  1         6  
  1         58  
12              
13 1     1   6 use Carp qw(croak);
  1         8  
  1         1821  
14              
15             our $VERSION = '0.01';
16              
17             sub TIEHASH {
18 1     1   12 my ($class) = @_;
19 1         6 return bless {}, $class;
20             }
21              
22             sub FETCH {
23 17     17   2031 my ($self, $key) = @_;
24             # check the obvious first.
25 17 100       49 if (exists($self->{$key})) {
26 8         33 return $self->{$key};
27             } else {
28             # now check each precision of this key starting with the highest precision
29 9         15 my $max_precision = (sort { $b <=> $a } keys %{$self->{_precision}})[0];
  6         17  
  9         45  
30 9         36 for (my $precision = $max_precision; $precision > 0; $precision--) {
31 6         37 my $k = sprintf("%.$precision" . "f", $key);
32 6 100       24 if (exists($self->{$k})) {
33 2         11 return $self->{$k};
34             }
35             }
36            
37             # ok one final one. check and see if it collapses into an int that we have.
38 7         25 my $k = sprintf('%d', $key);
39 7 100       24 if (exists($self->{$k})) {
40 1         11 return $self->{$k};
41             }
42             }
43 6         22 return undef;
44             }
45              
46             sub STORE {
47 6     6   1636 my ($self, $key, $value) = @_;
48            
49 6         28 my (@ranges) = split(/\s*,\s*/, $key);
50            
51             # blow up the range into a hash table, sorry memory, but i want fast lookups!
52 6         13 foreach my $range (@ranges) {
53 8 100       50 if ($range =~ /^(\-)?(\d+)\.?(\d*)\.\.(\-)?(\d+)\.?(\d*)$/) {
54 5         21 my ($a_sign, $a_num, $a_dec) = ($1, $2, $3);
55 5         17 my ($b_sign, $b_num, $b_dec) = ($4, $5, $6);
56            
57 5         6 my ($alpha, $beta, $precision);
58            
59 5 100 66     28 if ($a_dec || $b_dec) {
60             # this is a fp load, lets get the precision.
61 2 100       7 $precision = length($a_dec) > length($b_dec) ? length($a_dec) : length($b_dec);
62 2         26 $alpha = sprintf('%.' . $precision . 'f', "$a_sign$a_num.$a_dec");
63 2         13 $beta = sprintf('%.' . $precision . 'f',"$b_sign$b_num.$b_dec");
64             } else {
65 3         5 $alpha = "$a_sign$a_num";
66 3         6 $beta = "$b_sign$b_num";
67             }
68            
69 5 50       18 if ($beta > $alpha) {
70 5 100       12 if ($precision) {
71             # fp!
72 2         7 my $inc_by = "0." . "0" x ($precision - 1) . "1";
73 2         8 for (my $i = $alpha; $i <= $beta; $i += $inc_by) {
74 100219         272903 my $key = sprintf('%.' . $precision . 'f', $i);
75 100219         278252 $self->{$key} = $value;
76 100219 50       226878 $self->{0} = $value if sprintf("%d", $i) == 0;
77 100219         236377 $self->{_precision}->{$precision}++;
78             }
79             } else {
80             # straight int load.
81 3         9 for ($alpha..$beta) {
82 18         67 $self->{$_} = $value;
83             }
84             }
85             } else {
86 0         0 croak "$alpha is greater than $beta.. invalid range: $range\n";
87             }
88             } else {
89             # just one!
90 3 50       13 if ($range =~ /^(\-)?(\d+)\.?(\d*)/) {
91 3         9 my $precision = length($3);
92 3         10 $self->{$range} = $value;
93 3         16 $self->{_precision}->{$precision}++;
94             } else {
95             # its not even numeric
96 0         0 $self->{$range} = $value;
97             }
98             }
99             }
100             }
101              
102             sub CLEAR {
103 2     2   841 my ($self) = @_;
104 2         81076 %$self = ();
105             }
106              
107             sub DELETE {
108 2     2   530 my ($self, $key) = @_;
109            
110 2         9 my (@ranges) = split(/\s*,\s*/, $key);
111            
112             # blow up the range into a hash table, sorry memory, but i want fast lookups!
113 2         5 foreach my $range (@ranges) {
114 2 100       28 if ($range =~ /^(\-)?(\d+)\.?(\d*)\.\.(\-)?(\d+)\.?(\d*)$/) {
115 1         4 my ($a_sign, $a_num, $a_dec) = ($1, $2, $3);
116 1         4 my ($b_sign, $b_num, $b_dec) = ($4, $5, $6);
117            
118 1         5 my ($alpha, $beta, $precision);
119            
120 1 50 33     8 if ($a_dec || $b_dec) {
121             # this is a fp load, lets get the precision.
122 0 0       0 $precision = length($a_dec) > length($b_dec) ? length($a_dec) : length($b_dec);
123 0         0 $alpha = sprintf('%.' . $precision . 'f', "$a_sign$a_num.$a_dec");
124 0         0 $beta = sprintf('%.' . $precision . 'f',"$b_sign$b_num.$b_dec");
125             } else {
126 1         2 $alpha = "$a_sign$a_num";
127 1         2 $beta = "$b_sign$b_num";
128             }
129            
130 1 50       4 if ($beta > $alpha) {
131 1 50       3 if ($precision) {
132             # fp!
133 0         0 my $inc_by = "0." . "0" x ($precision - 1) . "1";
134 0         0 for (my $i = $alpha; $i <= $beta; $i += $inc_by) {
135 0         0 my $key = sprintf('%.' . $precision . 'f', $i);
136 0         0 delete($self->{$key});
137 0 0       0 delete($self->{0}) if sprintf("%d", $i) == 0;
138 0         0 $self->{_precision}->{$precision}--;
139             }
140             } else {
141             # straight int load.
142 1         3 for ($alpha..$beta) {
143 3         11 delete($self->{$_});
144             }
145             }
146             } else {
147 0         0 croak "$alpha is greater than $beta.. invalid range.\n";
148             }
149             } else {
150             # just one!
151 1 50       7 if ($range =~ /^(\-)?(\d+)\.?(\d*)/) {
152 1         3 my $precision = length($3);
153 1         3 delete($self->{$range});
154 1         7 $self->{_precision}->{$precision}--;
155             } else {
156             # not numeric, just delete
157 0         0 delete($self->{$range});
158             }
159             }
160             }
161             }
162              
163             sub EXISTS {
164 2     2   35077 my ($self, $key) = @_;
165 2 100       11 return defined($self->FETCH($key)) ? 1 : 0;
166             }
167              
168             sub SCALAR {
169             # return the object!
170 1     1   72 return($_[0]);
171             }
172              
173             sub FIRSTKEY {
174 2     2   6 my ($self) = @_;
175 2         5 my $a = keys %$self;
176 2         437 my $key = each %$self;
177 2   33     23 until ($key !~ /^_/o || !$key) {
178 0         0 $key = each %$self;
179             }
180 2         15 return $key;
181             }
182              
183             sub NEXTKEY {
184 2     2   4 my ($self) = @_;
185 2         35 my $key = each %$self;
186 2   66     21 until ($key !~ /^_/o || !$key) {
187 1         442 $key = each %$self;
188             }
189 2         13 return $key;
190             }
191              
192             1;
193             __END__