File Coverage

blib/lib/Data/BitStream/Code/Levenstein.pm
Criterion Covered Total %
statement 68 72 94.4
branch 20 28 71.4
condition 3 3 100.0
subroutine 8 8 100.0
pod 2 2 100.0
total 101 113 89.3


line stmt bran cond sub pod time code
1             package Data::BitStream::Code::Levenstein;
2 28     28   36500 use strict;
  28         64  
  28         1111  
3 28     28   160 use warnings;
  28         63  
  28         1475  
4             BEGIN {
5 28     28   68 $Data::BitStream::Code::Levenstein::AUTHORITY = 'cpan:DANAJ';
6 28         4398 $Data::BitStream::Code::Levenstein::VERSION = '0.08';
7             }
8              
9             our $CODEINFO = { package => __PACKAGE__,
10             name => 'Levenstein',
11             aliases => ['Levenshtein'],
12             universal => 1,
13             params => 0,
14             encodesub => sub {shift->put_levenstein(@_)},
15             decodesub => sub {shift->get_levenstein(@_)}, };
16              
17             sub _floorlog2_lev {
18 8656     8656   10733 my $d = shift;
19 8656         10410 my $base = 0;
20 8656         32869 $base++ while ($d >>= 1);
21 8656         21550 $base;
22             }
23              
24 28     28   167 use Moo::Role;
  28         65  
  28         254  
25             requires qw(read write get_unary1 put_unary1);
26              
27             # Levenstein code (also called Levenshtein).
28             #
29             # Early variable length code (1968), rarely used.
30             # Compares to Elias Omega and Even-Rodeh.
31             #
32             # See: V.E. Levenstein, "On the Redundancy and Delay of Separable Codes for the Natural Numbers," in Problems of Cybernetics v. 20 (1968), pp 173-179.
33             #
34             # Notes:
35             # This uses a 1-based unary coding. This matches the code definition,
36             # though is less efficient with most BitStream implementations.
37             #
38             # Given BitStream's 0-based Omega,
39             # length(levenstein(k+1)) == length(omega(k))+1 for all k >= 0
40             #
41              
42             sub put_levenstein {
43 816     816 1 20912 my $self = shift;
44              
45 816         1980 foreach my $v (@_) {
46 2343 100 100     10796 $self->error_code('zeroval') unless defined $v and $v >= 0;
47 2341 100       4533 if ($v == 0) { $self->write(1, 0); next; }
  75         280  
  75         159  
48              
49             # Simpler code:
50             # while ( (my $base = _floorlog2($val)) > 0) {
51             # unshift @d, [$base, $val];
52             # $val = $base;
53             # }
54             # $self->put_unary1(scalar @d + 1);
55             # foreach my $aref (@d) { $self->write( @{$aref} ); }
56              
57 2266         2726 my $val = $v;
58 2266         2467 my @d;
59 2266         2375 if (0) {
60             while ( (my $base = _floorlog2_lev($val)) > 0) {
61             unshift @d, [$base, $val];
62             $val = $base;
63             }
64             $self->put_unary1(scalar @d + 1);
65             } else {
66             # Bundle up groups of 32-bit writes.
67 2266         3300 my $cbits = 0;
68 2266         2286 my $cword = 0;
69 2266         3241 my $C = 1;
70 2266         4531 while ( (my $base = _floorlog2_lev($val)) > 0) {
71 6390         6866 $C++;
72 6390         8486 my $cval = $val & ~(1 << $base); # erase bit above base
73 6390 50       11984 if (($cbits + $base) >= 32) {
74 0 0       0 unshift @d, [$cbits, $cword] if $cbits > 0;
75 0         0 $cword = $cval;
76 0         0 $cbits = $base;
77             } else {
78 6390         7569 $cword |= ($cval << $cbits);
79 6390         7436 $cbits += $base;
80             }
81 6390         18371 $val = $base;
82             }
83 2266 100       7558 unshift @d, [$cbits, $cword] if $cbits > 0;;
84 2266         7497 $self->put_unary1($C);
85             }
86              
87 2266         3608 foreach my $aref (@d) { $self->write( @{$aref} ); }
  2233         2304  
  2233         7648  
88             }
89 814         2461 1;
90             }
91              
92             sub get_levenstein {
93 838     838 1 10971 my $self = shift;
94 838 50       2574 $self->error_stream_mode('read') if $self->writing;
95 838         1090 my $count = shift;
96 838 100       2033 if (!defined $count) { $count = 1; }
  817 50       1091  
    0          
97 21         53 elsif ($count < 0) { $count = ~0; } # Get everything
98 0         0 elsif ($count == 0) { return; }
99              
100 838         1093 my @vals;
101 838         2179 my $maxbits = $self->maxbits;
102 838         2379 $self->code_pos_start('Levenstein');
103 838         26430 while ($count-- > 0) {
104 2386         6522 $self->code_pos_set;
105              
106 2386         74593 my $C = $self->get_unary1;
107 2383 100       5779 last unless defined $C;
108              
109 2361         2619 my $val = 0;
110 2361 100       4803 if ($C > 0) {
111 2280         2752 my $N = 1;
112 2280         4659 for (1 .. $C-1) {
113 6404 100       11689 $self->error_code('overflow') if $N > $maxbits;
114 6403         16453 my $next = $self->read($N);
115 6403 50       13116 $self->error_off_stream unless defined $next;
116 6403         12726 $N = (1 << $N) | $next;
117             }
118 2279         3746 $val = $N;
119             }
120 2360         6745 push @vals, $val;
121             }
122 834         2405 $self->code_pos_end;
123 834 100       25893 wantarray ? @vals : $vals[-1];
124             }
125 28     28   38985 no Moo::Role;
  28         101  
  28         227  
126             1;
127              
128             # ABSTRACT: A Role implementing Levenstein codes
129              
130             =pod
131              
132             =encoding utf8
133              
134             =head1 NAME
135              
136             Data::BitStream::Code::Levenstein - A Role implementing Levenstein codes
137              
138             =head1 VERSION
139              
140             version 0.08
141              
142             =head1 DESCRIPTION
143              
144             A role written for L that provides get and set methods for
145             the Levenstein codes. The role applies to a stream object.
146              
147             These are also known as Levenshtein or Левенште́йн codes. They are often used
148             in situations where the Elias Omega, Even-Rodeh, or Fibonacci codes would be
149             considered.
150              
151             =head1 METHODS
152              
153             =head2 Provided Object Methods
154              
155             =over 4
156              
157             =item B< put_levenstein($value) >
158              
159             =item B< put_levenstein(@values) >
160              
161             Insert one or more values as Levenstein codes. Returns 1.
162              
163             =item B< get_levenstein() >
164              
165             =item B< get_levenstein($count) >
166              
167             Decode one or more Levenstein codes from the stream. If count is omitted,
168             one value will be read. If count is negative, values will be read until
169             the end of the stream is reached. In scalar context it returns the last
170             code read; in array context it returns an array of all codes read.
171              
172             =back
173              
174             =head2 Required Methods
175              
176             =over 4
177              
178             =item B< read >
179              
180             =item B< write >
181              
182             =item B< get_unary1 >
183              
184             =item B< put_unary1 >
185              
186             These methods are required for the role.
187              
188             =back
189              
190             =head1 SEE ALSO
191              
192             =over 4
193              
194             =item V.E. Levenstein, "On the Redundancy and Delay of Separable Codes for the Natural Numbers," in Problems of Cybernetics v. 20 (1968), pp 173-179.
195              
196             =back
197              
198             =head1 AUTHORS
199              
200             Dana Jacobsen Edana@acm.orgE
201              
202             =head1 COPYRIGHT
203              
204             Copyright 2011 by Dana Jacobsen Edana@acm.orgE
205              
206             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
207              
208             =cut