File Coverage

blib/lib/Data/BitStream/Code/Golomb.pm
Criterion Covered Total %
statement 73 74 98.6
branch 42 50 84.0
condition 6 12 50.0
subroutine 7 7 100.0
pod 2 2 100.0
total 130 145 89.6


line stmt bran cond sub pod time code
1             package Data::BitStream::Code::Golomb;
2 28     28   23798 use strict;
  28         66  
  28         1041  
3 28     28   159 use warnings;
  28         60  
  28         1428  
4             BEGIN {
5 28     28   66 $Data::BitStream::Code::Golomb::AUTHORITY = 'cpan:DANAJ';
6 28         3534 $Data::BitStream::Code::Golomb::VERSION = '0.08';
7             }
8              
9             our $CODEINFO = { package => __PACKAGE__,
10             name => 'Golomb',
11             universal => 0,
12             params => 1,
13             encodesub => sub {shift->put_golomb(@_)},
14             decodesub => sub {shift->get_golomb(@_)}, };
15              
16 28     28   161 use Moo::Role;
  28         71  
  28         225  
17             requires qw(read write put_unary get_unary);
18              
19             # Usage:
20             #
21             # $stream->put_golomb( $m, $value );
22             #
23             # encode $value using Golomb coding. The quotient of $value / $m is encoded
24             # with Unary, and the remainder is written in truncated binary form.
25             #
26             # Note that Rice(k) = Golomb(2^k). Hence if $m is a power of 2, then this
27             # will be equal to the Rice code of log2(m).
28             #
29             # $stream->put_golomb( sub { my $self=shift; $self->put_gamma(@_); }, $m, $value );
30             #
31             # This form allows Golomb coding with any integer coding method replacing
32             # Unary coding. The most common use of this is Gamma encoding, but interesting
33             # results can be obtained with Delta and Fibonacci codes as well.
34              
35             sub put_golomb {
36 7354     7354 1 106343 my $self = shift;
37 7354 100       23240 my $sub = shift if ref $_[0] eq 'CODE'; ## no critic
38 7354         9986 my $m = shift;
39 7354 50       20868 $self->error_code('param', 'm must be >= 1') unless $m >= 1;
40              
41 7354 100       15634 return( (defined $sub) ? $sub->($self, @_) : $self->put_unary(@_) ) if $m==1;
    100          
42 7352         10431 my $b = 1;
43 7352         15011 { my $v = $m-1; $b++ while ($v >>= 1); } # $b is ceil(log2($m))
  7352         9905  
  7352         57321  
44 7352         11600 my $threshold = (1 << $b) - $m; # will be 0 if m is a power of 2
45              
46 7352         12318 foreach my $val (@_) {
47 27989 100 100     124600 $self->error_code('zeroval') unless defined $val and $val >= 0;
48              
49             # Obvious but incorrect for large values (you'll get negative r values).
50             # my $q = int($val / $m);
51             # my $r = $val - $q * $m;
52             # Correct way:
53 27979         38660 my $r = $val % $m;
54 27979         47390 my $q = ($val - $r) / $m;
55             # Make sure modulo works as intended
56 27979 50 33     238842 $self->error_code('assert') unless ($r >= 0) && ($r < $m) && ($q==int($q)) && (($q*$m+$r) == $val);
      33        
      33        
57              
58 27979 100       101531 (defined $sub) ? $sub->($self, $q) : $self->put_unary($q);
59              
60 27979 100       57362 if ($r < $threshold) {
61 13599         49532 $self->write($b-1, $r);
62             } else {
63 14380         50193 $self->write($b, $r + $threshold);
64             }
65             }
66 7342         24684 1;
67             }
68             sub get_golomb {
69 7458     7458 1 91668 my $self = shift;
70 7458 100       21696 my $sub = shift if ref $_[0] eq 'CODE'; ## no critic
71 7458         10532 my $m = shift;
72 7458 50       19917 $self->error_code('param', 'm must be >= 1') unless $m >= 1;
73              
74 7458 100       15358 return( (defined $sub) ? $sub->($self, @_) : $self->get_unary(@_) ) if $m==1;
    100          
75 7456         8991 my $b = 1;
76 7456         9381 { my $v = $m-1; $b++ while ($v >>= 1); } # $b is ceil(log2($m))
  7456         9312  
  7456         54235  
77 7456         10132 my $threshold = (1 << $b) - $m; # will be 0 if m is a power of 2
78              
79 7456         9214 my $count = shift;
80 7456 100       24300 if (!defined $count) { $count = 1; }
  7249 50       9655  
    0          
81 207         430 elsif ($count < 0) { $count = ~0; } # Get everything
82 0         0 elsif ($count == 0) { return; }
83              
84 7456         13433 my @vals;
85 7456         23303 $self->code_pos_start('Golomb');
86 7456 100       237355 if ($threshold == 0) {
87 1674         4829 while ($count-- > 0) {
88 5922         16008 $self->code_pos_set;
89 5922 100       200080 my $q = (defined $sub) ? $sub->($self) : $self->get_unary();
90 5922 100       13753 last unless defined $q;
91 5875         7951 my $val = $q * $m;
92 5875         16507 my $remainder = $self->read($b);
93 5875 50       14194 $self->error_off_stream unless defined $remainder;
94 5875         16659 push @vals, $val+$remainder;
95             }
96             } else {
97 5782         15680 while ($count-- > 0) {
98 22378         70026 $self->code_pos_set;
99 22378 100       765495 my $q = (defined $sub) ? $sub->($self) : $self->get_unary();
100 22370 100       65322 last unless defined $q;
101 22205         38122 my $val = $q * $m;
102 22205         77710 my $remainder = $self->read($b-1);
103 22205 100       49913 $self->error_off_stream unless defined $remainder;
104 22204 100       44395 if ($remainder >= $threshold) {
105 8577         23002 my $extra = $self->read(1);
106 8577 50       21772 $self->error_off_stream unless defined $extra;
107 8577         13271 $remainder = ($remainder << 1) + $extra - $threshold;
108             }
109 22204         62399 push @vals, $val+$remainder;
110             }
111             }
112 7447         20061 $self->code_pos_end;
113 7447 100       232724 wantarray ? @vals : $vals[-1];
114             }
115 28     28   53821 no Moo::Role;
  28         85  
  28         193  
116             1;
117              
118             # ABSTRACT: A Role implementing Golomb codes
119              
120             =pod
121              
122             =head1 NAME
123              
124             Data::BitStream::Code::Golomb - A Role implementing Golomb codes
125              
126             =head1 VERSION
127              
128             version 0.08
129              
130             =head1 DESCRIPTION
131              
132             A role written for L that provides get and set methods for
133             Golomb codes. The role applies to a stream object.
134              
135             Beware that with the default unary coding for the quotient, these codes can
136             become extraordinarily long for values much larger than C.
137              
138             =head1 METHODS
139              
140             =head2 Provided Object Methods
141              
142             =over 4
143              
144             =item B< put_golomb($m, $value) >
145              
146             =item B< put_golomb($m, @values) >
147              
148             Insert one or more values as Golomb codes with parameter m. Returns 1.
149              
150             =item B< put_golomb(sub { ... }, $m, @values) >
151              
152             Insert one or more values as Golomb codes using the user provided subroutine
153             instead of the traditional Unary code for the base. For example, the common
154             Gamma-Golomb encoding can be performed using the sub:
155              
156             sub { shift->put_gamma(@_); }
157              
158             =item B< get_golomb($m) >
159              
160             =item B< get_golomb($m, $count) >
161              
162             Decode one or more Golomb codes from the stream. If count is omitted,
163             one value will be read. If count is negative, values will be read until
164             the end of the stream is reached. In scalar context it returns the last
165             code read; in array context it returns an array of all codes read.
166              
167             =item B< get_golomb(sub { ... }, $m) >
168              
169             Similar to the regular get method except using the user provided subroutine
170             instead of unary encoding the base. For example:
171              
172             sub { shift->get_gamma(@_); }
173              
174             =back
175              
176             =head2 Parameters
177              
178             The parameter C must be an integer greater than or equal to 1.
179              
180             The quotient of C is encoded using unary (or via the user
181             supplied subroutine), followed by the remainder in truncated binary form.
182              
183             Note: if C then the result will be coded purely using unary (or the
184             supplied sub) coding.
185              
186             Note: if C is a power of 2 (C for some non-negative integer
187             C), then the result is equal to the simpler C code, where the
188             operations devolve into a shift and mask.
189              
190             For a general array of integers, the value of C leading to the smallest sum
191             of codes is approximately 0.69 * the average of the values. (citation needed)
192              
193             Golomb coding is often preceeded by a step that adapts the parameter to the
194             data seen so far.
195              
196             =head2 Required Methods
197              
198             =over 4
199              
200             =item B< read >
201              
202             =item B< write >
203              
204             =item B< get_unary >
205              
206             =item B< put_unary >
207              
208             These methods are required for the role.
209              
210             =back
211              
212             =head1 SEE ALSO
213              
214             =over 4
215              
216             =item L
217              
218             =item L
219              
220             =item L
221              
222             =item L
223              
224             =item S.W. Golomb, "Run-length encodings", IEEE Transactions on Information Theory, vol 12, no 3, pp 399-401, 1966.
225              
226             =item R.F. Rice and R. Plaunt, "Adaptive Variable-Length Coding for Efficient Compression of Spacecraft Television Data", IEEE Transactions on Communications, vol 16, no 9, pp 889-897, Dec. 1971.
227              
228             =back
229              
230             =head1 AUTHORS
231              
232             Dana Jacobsen
233              
234             =head1 COPYRIGHT
235              
236             Copyright 2011-2012 by Dana Jacobsen
237              
238             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
239              
240             =cut