File Coverage

blib/lib/Data/BitStream/Code/BoldiVigna.pm
Criterion Covered Total %
statement 92 93 98.9
branch 29 36 80.5
condition 10 12 83.3
subroutine 8 8 100.0
pod 2 2 100.0
total 141 151 93.3


line stmt bran cond sub pod time code
1             package Data::BitStream::Code::BoldiVigna;
2 28     28   30453 use strict;
  28         63  
  28         1164  
3 28     28   161 use warnings;
  28         58  
  28         1683  
4             BEGIN {
5 28     28   69 $Data::BitStream::Code::BoldiVigna::AUTHORITY = 'cpan:DANAJ';
6 28         2760 $Data::BitStream::Code::BoldiVigna::VERSION = '0.08';
7             }
8              
9             our $CODEINFO = { package => __PACKAGE__,
10             name => 'BoldiVigna',
11             universal => 1,
12             params => 1,
13             encodesub => sub {shift->put_boldivigna(@_)},
14             decodesub => sub {shift->get_boldivigna(@_)}, };
15              
16 28     28   167 use Moo::Role;
  28         53  
  28         304  
17             requires qw(read write put_unary get_unary maxbits get_gamma put_gamma);
18              
19             # Boldi-Vigna Zeta codes.
20              
21             # Holds cached calculated parameters for each k
22             my @hp_cache;
23              
24             # Calculates parameters for a given k and maxbits.
25             sub _hparam_map {
26 13     13   36 my $k = shift;
27 13         32 my $maxbits = shift;
28              
29             #my $maxh = 0;
30             #$maxh++ while ($k * ($maxh+1)) < $maxbits;
31 13         67 my $maxh = int( ($maxbits-1) / $k );
32 13         31 my $maxhk = $maxh * $k;
33              
34 13         28 my @hparams; # stores [s threshold] for each h
35 13         44 foreach my $h (0 .. $maxh) {
36 416         472 my $hk = $h*$k;
37 416         552 my $interval = (1 << ($hk+$k)) - (1 << $hk) - 1;
38 416         511 my $z = $interval+1;
39 416         395 my $s = 1;
40 416         381 { my $v = $z; $s++ while ($v >>= 1); } # ceil log2($z)
  416         400  
  416         10045  
41 416         544 my $threshold = (1 << $s) - $z;
42 416         1145 $hparams[$h] = [ $s, $threshold ];
43             #print "storing params for h=$h [ $s, $threshold ]\n";
44             }
45              
46 13         71 return $maxhk, \@hparams;
47             }
48              
49             sub put_boldivigna {
50 818     818 1 13970 my $self = shift;
51 818         1479 my $k = shift;
52 818 50 33     5111 $self->error_code('param', "k must be in range 1-15") if $k < 1 || $k > 15;
53              
54 818 50       2259 return $self->put_gamma(@_) if $k == 1;
55              
56 818         1630 my($maxhk, $hparams);
57 818 100       2139 if (defined $hp_cache[$k]) {
58 806         1064 ($maxhk, $hparams) = @{$hp_cache[$k]};
  806         5631  
59             } else {
60 12         61 ($maxhk, $hparams) = _hparam_map($k, $self->maxbits);
61 12         82 $hp_cache[$k] = [$maxhk, $hparams];
62             }
63 818         3298 my $maxval = $self->maxval;
64              
65 818         1836 foreach my $v (@_) {
66 3494 100 100     23419 $self->error_code('zeroval') unless defined $v and $v >= 0;
67              
68 3492 100       7169 if ($v == $maxval) {
69 6         22 $self->put_unary( ($maxhk/$k)+1 );
70 6         13 next;
71             }
72              
73 3486         4141 my $hk = 0;
74 3486   100     87779 $hk += $k while ( ($hk < $maxhk) && ($v >= ((1 << ($hk+$k))-1)) );
75 3486         5424 my $h = $hk/$k;
76 3486         9861 $self->put_unary($h);
77              
78 3486         5456 my $x = $v - (1 << $hk) + 1;
79             # Encode $x using "minimal binary code"
80 3486         3610 my ($s, $threshold) = @{$hparams->[$h]};
  3486         6876  
81             #print "using params for h=$h [ $s, $threshold ]\n";
82 3486 100       6271 if ($x < $threshold) {
83             #print "minimal code $x in ", $s-1, " bits\n";
84 1912         5661 $self->write($s-1, $x);
85             } else {
86             #print "minimal code $x => ", $x+$threshold, " in $s bits\n";
87 1574         5035 $self->write($s, $x+$threshold);
88             }
89             }
90 816         4487 1;
91             }
92             sub get_boldivigna {
93 823     823 1 16565 my $self = shift;
94 823         1232 my $k = shift;
95 823 100 100     4973 $self->error_code('param', "k must be in range 1-15") if $k < 1 || $k > 15;
96              
97 821 50       3037 return $self->get_gamma(@_) if $k == 1;
98              
99 821         1007 my $count = shift;
100 821 100       1825 if (!defined $count) { $count = 1; }
  797 50       1970  
    0          
101 24         51 elsif ($count < 0) { $count = ~0; } # Get everything
102 0         0 elsif ($count == 0) { return; }
103              
104 821         991 my($maxhk, $hparams);
105 821 100       1818 if (defined $hp_cache[$k]) {
106 820         1207 ($maxhk, $hparams) = @{$hp_cache[$k]};
  820         2227  
107             } else {
108 1         7 ($maxhk, $hparams) = _hparam_map($k, $self->maxbits);
109 1         4 $hp_cache[$k] = [$maxhk, $hparams];
110             }
111              
112 821         1290 my @vals;
113 821         3401 $self->code_pos_start('BoldiVigna');
114 821         27875 while ($count-- > 0) {
115 3521         9576 $self->code_pos_set;
116 3521         112029 my $h = $self->get_unary();
117 3518 100       7686 last unless defined $h;
118 3493 100       8240 if ($h > ($maxhk/$k)) {
119 6         28 push @vals, $self->maxval;
120 6         16 next;
121             }
122 3487         3624 my ($s, $threshold) = @{$hparams->[$h]};
  3487         7246  
123              
124 3487         10912 my $first = $self->read($s-1);
125 3487 100       7395 $self->error_off_stream unless defined $first;
126 3486 100       6988 if ($first >= $threshold) {
127 1574         4478 my $extra = $self->read(1);
128 1574 50       3238 $self->error_off_stream unless defined $extra;
129 1574         2493 $first = ($first << 1) + $extra - $threshold;
130             }
131 3486         5459 my $val = (1 << $h*$k) + $first - 1;
132 3486         9760 push @vals, $val;
133             }
134 817         2462 $self->code_pos_end;
135 817 100       29132 wantarray ? @vals : $vals[-1];
136             }
137 28     28   52085 no Moo::Role;
  28         66  
  28         289  
138             1;
139              
140             # ABSTRACT: A Role implementing the Zeta codes of Boldi and Vigna
141              
142             =pod
143              
144             =head1 NAME
145              
146             Data::BitStream::Code::BoldiVigna - A Role implementing Zeta codes
147              
148             =head1 VERSION
149              
150             version 0.08
151              
152             =head1 DESCRIPTION
153              
154             A role written for L that provides get and set methods for
155             Zeta codes of Paolo Boldi and Sebastiano Vigna. These codes are useful for
156             integers distributed as a power law with small exponent (smaller than 2).
157             The role applies to a stream object.
158              
159             =head1 METHODS
160              
161             =head2 Provided Object Methods
162              
163             =over 4
164              
165             =item B< put_boldivigna($k, $value) >
166              
167             =item B< put_boldivigna($k, @values) >
168              
169             Insert one or more values as Zeta_k codes. Returns 1.
170              
171             =item B< get_boldivigna($k) >
172              
173             =item B< get_boldivigna($k, $count) >
174              
175             Decode one or more Zeta_k codes from the stream. If count is omitted,
176             one value will be read. If count is negative, values will be read until
177             the end of the stream is reached. In scalar context it returns the last
178             code read; in array context it returns an array of all codes read.
179              
180             =back
181              
182             =head2 Parameters
183              
184             The parameter k must be between 1 and maxbits (32 or 64).
185              
186             C is equivalent to Elias Gamma coding.
187              
188             For values of C 6> the Elias Delta code will be better.
189              
190             Typical k values are between 2 and 6.
191              
192             =head2 Required Methods
193              
194             =over 4
195              
196             =item B< maxbits >
197              
198             =item B< read >
199              
200             =item B< write >
201              
202             =item B< get_unary >
203              
204             =item B< put_unary >
205              
206             =item B< get_gamma >
207              
208             =item B< put_gamma >
209              
210             These methods are required for the role.
211              
212             =back
213              
214             =head1 SEE ALSO
215              
216             =over 4
217              
218             =item Paolo Boldi and Sebastiano Vigna, "Codes for the World Wide Web", Internet Math, Vol 2, No 4, pp 407-429, 2005.
219              
220             =item L
221              
222             =back
223              
224             =head1 AUTHORS
225              
226             Dana Jacobsen
227              
228             =head1 COPYRIGHT
229              
230             Copyright 2011 by Dana Jacobsen
231              
232             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
233              
234             =cut