File Coverage

blib/lib/Tie/TimeSeries.pm
Criterion Covered Total %
statement 120 123 97.5
branch 30 34 88.2
condition 7 9 77.7
subroutine 22 22 100.0
pod 7 8 87.5
total 186 196 94.9


line stmt bran cond sub pod time code
1             # =============================================================================
2             package Tie::TimeSeries;
3             # -----------------------------------------------------------------------------
4             $Tie::TimeSeries::VERSION = '0.01';
5             # -----------------------------------------------------------------------------
6 4     4   113594 use 5.006;
  4         15  
  4         173  
7 4     4   23 use strict;
  4         9  
  4         144  
8 4     4   22 use warnings FATAL => 'all';
  4         18  
  4         220  
9              
10             =head1 NAME
11              
12             Tie::TimeSeries - Convenient hash tyng for time series data.
13              
14             =head1 SYNOPSIS
15              
16             use Tie::TimeSeries;
17              
18             # Using with tie()
19             tie %data, 'Tie::TimeSeries';
20             $data{ time() } = [ 0.14, 0.06, 0.01 ];
21              
22             %data = ();
23             $data{1361970900} = 10;
24             $data{1361971200} = 20;
25             $data{1361971500} = 30;
26             $,=',';
27             print values %data; # 10,20,30
28              
29             $data{1361971050} = 5;
30             print values %data; # 10,5,20,30
31              
32             # OO
33             $data = Tie::TimeSeries->new(
34             1361970900 => 10,
35             1361971200 => 20,
36             1361971500 => 30,
37             );
38             print $data->values(); # 10,20,30
39             $data->store( 1361971050 => 15 );
40             print $data->values(); # 15,20,30
41             $data->store( 1361971800 => 40 );
42             print $data->values(); # 15,20,30,40
43              
44              
45             =head1 DESCRIPTION
46              
47             When using time series data like throughput, statistics or so, this module is
48             convenient that key will be sorted automatically.
49             And this also is able to provide instance for OO using.
50              
51             =cut
52              
53              
54             # =============================================================================
55 4     4   23 use Carp;
  4         6  
  4         280  
56 4     4   4659 use Search::Binary;
  4         124  
  4         229  
57              
58 4     4   23 use base qw/ Tie::Hash /;
  4         8  
  4         10093  
59              
60             # =============================================================================
61              
62             =head1 STANDARD TIE HASH
63              
64             There are standard interfaces usage with tie().
65              
66             tie %yourhash, 'Tie::TimeSeries' [, $time => $value [, ... ]]
67              
68             On this way, use C<%yourhash> as regular Perl hashes, but keys and values will be stored in order internally.
69              
70             With several arguments given, initialization stores them. C<$time> must be
71             integer number. C<$value> can be any type of scalar.
72              
73             =cut
74              
75             # -----------------------------------------------------------------------------
76             sub TIEHASH {
77 20     20   39926 my $class = shift;
78              
79 20         131 my $self = {
80             h => {}, # Hash for fetching which has same data of $self->{d} has.
81             t => [], # Array of key means time; [ t1, t2, ..., tx ]
82             d => [], # Array of data warpped array;[ [v1],[v2], ..., [vx] ]
83             c => 0, # for each()
84             _i => 0, # for _readindex(), last read index
85             };
86              
87 20         57 bless $self, $class;
88 20         65 while ( @_ ){
89 8267         19443 $self->STORE( shift, shift );
90             }
91 20         112 return $self;
92             }
93              
94              
95             sub FETCH {
96 8281     8281   13581 my ($self, $key) = (@_);
97              
98 8281 100 66     65129 return (defined $key && CORE::exists($self->{h}{$key}))? $self->{h}{$key}->[0]: undef;
99             }
100              
101              
102             sub STORE {
103 8287     8287   13086 my ($self, $key, $val) = (@_);
104              
105             # Validation
106 8287 100 66     51948 unless ( defined($key) && $key =~ /^\d+$/ ){
107 4         1198 carp("Not a number given as key of hash.");
108 4         146 return;
109             }
110              
111             # Storing
112 8283 100       21190 if ( CORE::exists $self->{h}{$key} ){
113 10         11 push @{ $self->{h}{$key} }, $val;
  10         20  
114 10         10 shift @{ $self->{h}{$key} };
  10         29  
115              
116             } else {
117 8273         18693 my $d = [ $val ];
118              
119             # In the case of inserting just ordered data by number as key,
120             # this cheking will be effective...
121 8273         9012 my $nums = $#{$self->{t}};
  8273         15468  
122 8273 100 100     37993 if ( $nums >= 0 && $key > $self->{t}[ $nums ] ){
123 4158         4068 push @{ $self->{t} }, $key+0;
  4158         7233  
124 4158         4185 push @{ $self->{d} }, $d;
  4158         6828  
125             }
126             else {
127             # Caluculation prefer index of array to insert
128 4115         4724 my $posi = binary_search(0, $#{$self->{t}}, $key, \&_readindex, $self);
  4115         14905  
129 4115         28545 splice @{ $self->{t} }, $posi, 0, $key+0;
  4115         22918  
130 4115         5332 splice @{ $self->{d} }, $posi, 0, $d;
  4115         19176  
131             }
132 8273         39669 $self->{h}{$key} = $d;
133             }
134             }
135              
136              
137             sub DELETE {
138 11     11   1331 my ($self, $key) = (@_);
139              
140 11 100       39 if ( CORE::exists $self->{h}{$key} ){
141             # Seek index
142 8         12 my $posi = binary_search(0, $#{$self->{t}}, $key, \&_readindex, $self);
  8         38  
143 8         52 splice @{ $self->{t} }, $posi, 1;
  8         21  
144 8         14 splice @{ $self->{d} }, $posi, 1;
  8         15  
145 8         18 my $val = $self->{h}{$key}->[0];
146 8         21 CORE::delete $self->{h}{$key};
147 8         32 return $val;
148             }
149 3         10 return undef;
150             }
151              
152             sub EXISTS {
153 5     5   15 my ($self, $key) = (@_);
154 5 100       41 return CORE::exists( $self->{h}{$key} )? 1: undef;
155             }
156              
157              
158             sub FIRSTKEY {
159 25     25   11591 my ($self, $key) = (@_);
160              
161 25         48 $self->{c} = 0;
162 25         55 $self->NEXTKEY();
163             }
164              
165              
166             sub NEXTKEY {
167 16503     16503   19272 my ($self) = (@_);
168              
169 16503 100       19144 return $self->{c} <= $#{$self->{t}}? $self->{t}[ $self->{c}++ ]: undef;
  16503         72896  
170             }
171              
172              
173             # =============================================================================
174              
175             =head1 OBJECTIVE USAGE
176              
177             This modules provides object instance and methods.
178              
179             =head2 CONSTRUCTOR
180              
181             $tied = tie %yourhash, 'Tie::TimeSeries' [, $time => $value [, ... ]]
182             $tied = Tie::TimeSeries->new( $time => $value [, ... ] );
183              
184             Call method C to get instance or get return value of tie().
185              
186             =cut
187              
188              
189             # -----------------------------------------------------------------------------
190             # OO Methods
191             # -----------------------------------------------------------------------------
192             sub new {
193 7     7 0 10380 TIEHASH( @_ );
194             }
195              
196              
197             # =============================================================================
198              
199             =head2 METHODS
200              
201             =head3 fetch()
202              
203             Method C will fetch a value bound specified key.
204              
205             $tied->fetch( $time [, ... ] );
206             $tied->fetch( \@keys_array );
207              
208             =cut
209              
210             # -----------------------------------------------------------------------------
211             sub fetch {
212 17     17 1 1143 my $self = shift;
213              
214 17         26 my @ret = ();
215              
216 17 100       38 if ( ref($_[0]) eq 'ARRAY' ){
217 1         3 foreach ( @{$_[0]} ){
  1         4  
218 3         7 push @ret, $self->FETCH( $_ );
219             }
220             }
221             else {
222 16         36 while ( @_ ){
223 18         41 push @ret, $self->FETCH( shift );
224             }
225             }
226 17 100       28 if ( wantarray ){
227 3         17 return @ret;
228             } else {
229 14 50       28 if ( @ret == 1 ){
230 14         63 return $ret[0];
231             } else {
232 0         0 return \@ret;
233             }
234             }
235             }
236              
237              
238             # =============================================================================
239              
240             =head3 store()
241              
242             Method C will store keys of time and values to the object.
243              
244             $tied->store( $time => $value [, ... ] );
245             $tied->store( \%pairs_hash );
246              
247             =cut
248              
249             # -----------------------------------------------------------------------------
250             sub store {
251 5     5 1 8 my $self = shift;
252              
253 5 100       16 if ( ref($_[0]) eq 'HASH' ){
254 3         4 while ( my ($k, $v) = each %{$_[0]} ){
  12         38  
255 9         14 $self->STORE( $k, $v );
256             }
257             }
258             else {
259 2         5 while ( @_ ){
260 4         17 $self->STORE( shift, shift );
261             }
262             }
263             }
264              
265              
266             # =============================================================================
267              
268             =head3 delete()
269              
270             Method C will remove key and value from the object.
271              
272             $tied->delete( $time [, ... ] );
273             $tied->delete( \@keys_array );
274              
275             And this method will return deleted value(s).
276              
277             =cut
278              
279             # -----------------------------------------------------------------------------
280             sub delete {
281 4     4 1 1459 my $self = shift;
282              
283 4         9 my @deleted = ();
284              
285 4 100       13 if ( ref($_[0]) eq 'ARRAY' ){
286 1         3 foreach ( @{$_[0]} ){
  1         5  
287 2         5 push @deleted, $self->DELETE( $_ );
288             }
289             }
290             else {
291 3         9 while ( @_ ){
292 4         13 push @deleted, $self->DELETE( shift );
293             }
294             }
295 4 100       12 if ( wantarray ){
296 2         11 return @deleted;
297             } else {
298 2 50       6 if ( @deleted == 1 ){
299 2         7 return $deleted[0];
300             } else {
301 0         0 return \@deleted;
302             }
303             }
304             }
305              
306              
307             # =============================================================================
308              
309             =head3 exists()
310              
311             Method C returns boolean value.
312              
313             $tied->exists( $time );
314              
315             =cut
316              
317             # -----------------------------------------------------------------------------
318             sub exists {
319 3     3 1 13 my $self = shift;
320              
321 3         9 return $self->EXISTS(shift);
322             }
323              
324              
325             # =============================================================================
326              
327             =head3 keys()
328              
329             Method C returns keys list of the object.
330              
331             $tied->keys();
332              
333             =cut
334              
335             # -----------------------------------------------------------------------------
336             sub keys {
337 3     3 1 376 return @{(shift)->{t}};
  3         24  
338             }
339              
340              
341             # =============================================================================
342              
343             =head3 values()
344              
345             Method C returns values list of the object.
346              
347             $tied->values();
348              
349             =cut
350              
351             # -----------------------------------------------------------------------------
352             sub values {
353 6     6 1 1298 return map { $_->[0] } @{(shift)->{d}};
  16         45  
  6         22  
354             }
355              
356              
357             # =============================================================================
358              
359             =head3 iterate()
360              
361             Method C execute a routine for each keys and values.
362              
363             $tied->iterate(\&subroutine);
364              
365             Given subroutine will call by iterator with two argument, key and value.
366              
367             # Iterator example
368             $obj->iterate(sub {
369             ($key, $val) = @_;
370             $obj->{$key} = $val * 100;
371             });
372              
373             =cut
374              
375             # -----------------------------------------------------------------------------
376             sub iterate {
377 1     1 1 15 my ($self, $func) = @_;
378 1 50       5 unless ( ref($func) eq 'CODE' ){
379 0         0 croak("Not a subrotine was given to iterate().");
380             }
381              
382 1         1 foreach my $key ( @{$self->{t}} ){
  1         4  
383 5         23 $func->( $key, $self->FETCH($key) );
384             }
385             }
386              
387              
388             # =============================================================================
389              
390              
391             # -----------------------------------------------------------------------------
392             # Private functions
393             # -----------------------------------------------------------------------------
394             sub _readindex {
395 764898     764898   4791630 my ($self, $val, $posi ) = @_;
396              
397 764898 100       1257041 if ( defined $posi ){
398 12799         16480 $self->{_i} = $posi;
399 12799         49182 return ( $val <=> $self->{t}[$posi], $posi );
400             } else {
401 752099 50       897213 return $self->{_i} <= $#{$self->{t}}?
  752099         3462536  
402             ( $val <=> $self->{t}[ $self->{_i} ], $self->{_i}++ ):
403             ( -1, $self->{_i}++ );
404             }
405             }
406              
407              
408             # -----------------------------------------------------------------------------
409              
410              
411             =head1 SEE ALSO
412              
413             See L<> - The great module brings many hints to this module.
414              
415              
416             =head1 AUTHOR
417              
418             Takahiro Onodera, C<< >>
419              
420              
421             =head1 LICENSE AND COPYRIGHT
422              
423             Copyright 2010 T.Onodera.
424              
425             This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License.
426              
427             See http://dev.perl.org/licenses/ for more information.
428              
429             =cut
430              
431             1;