File Coverage

blib/lib/Net/OnlineCode/Decoder.pm
Criterion Covered Total %
statement 24 98 24.4
branch 0 18 0.0
condition 0 9 0.0
subroutine 8 13 61.5
pod 0 5 0.0
total 32 143 22.3


line stmt bran cond sub pod time code
1             package Net::OnlineCode::Decoder;
2              
3 1     1   15617 use strict;
  1         2  
  1         46  
4 1     1   6 use warnings;
  1         2  
  1         36  
5              
6 1     1   6 use Carp;
  1         3  
  1         103  
7              
8 1     1   381 use Net::OnlineCode;
  1         2  
  1         51  
9 1     1   496 use Net::OnlineCode::GraphDecoder;
  1         2  
  1         29  
10 1     1   7 use Net::OnlineCode::Bones;
  1         1  
  1         23  
11              
12             require Exporter;
13              
14 1     1   3 use vars qw(@ISA @EXPORT_OK @EXPORT %EXPORT_TAGS $VERSION);
  1         2  
  1         93  
15              
16             # Inherit from base class
17             @ISA = qw(Net::OnlineCode Exporter);
18             @EXPORT_OK = qw();
19              
20             $VERSION = '0.04';
21              
22 1     1   4 use constant DEBUG => 0;
  1         1  
  1         797  
23              
24             sub new {
25              
26 0     0 0   my $class = shift;
27              
28 0           my %opts = (
29             # decoder-specific arguments:
30             expand_aux => 0, # override parent class's default
31             expand_msg => 1, # expand_* options used by expansion()
32             initial_rng => undef,
33             # user-supplied arguments:
34             @_
35             );
36 0 0         unless (ref($opts{initial_rng})) {
37 0           carp "$class->new requires an initial_rng => \$rng parameter\n";
38 0           return undef;
39             }
40              
41             # Send all arguments to the base class. It does basic parameter
42             # handling/mangling, calculates the number of auxiliary blocks based
43             # on them and generates a probability distribution.
44              
45 0           my $self = $class->SUPER::new(@_);
46              
47             # Our subclass includes extra data/options
48 0           $self->{expand_msg}=$opts{expand_msg};
49 0           my $graph = Net::OnlineCode::GraphDecoder->new
50             (
51             $self->{mblocks},
52             $self->{ablocks},
53             $self->auxiliary_mapping($opts{initial_rng}),
54             );
55 0           $self->{graph} = $graph;
56              
57             # print "Decoder: returning from constructor\n";
58 0           return $self;
59              
60             }
61              
62             sub accept_check_block {
63 0     0 0   my $self = shift;
64 0           my $rng = shift;
65              
66             # print "Decoder: calling checkblock_mapping\n";
67 0           my $composite_blocks = $self->checkblock_mapping($rng);
68              
69 0           print "Decoder check block: " . (join " ", @$composite_blocks)
70             . "\n" if DEBUG;
71              
72             # print "Decoder: Adding check block to graph\n";
73 0           my $check_node = $self->{graph}->add_check_block($composite_blocks);
74              
75 0           ++($self->{chblocks});
76              
77             # Caller will now have to call resolve manually ...
78             # print "Decoder: Resolving graph\n";
79             # ($self->{graph}->resolve($check_node));
80              
81             # caller probably won't use return value, but if they do it makes
82             # sense to return zero-based array index
83            
84 0           return $check_node - $self->get_coblocks;
85              
86             }
87              
88             # pass calls to resolve onto graph decoder object
89             sub resolve {
90 0     0 0   my ($self,@args) = @_;
91              
92 0           $self->{graph}->resolve(@args);
93             }
94              
95             # new routine to replace xor_list; does "lazy" expansion of node lists
96             # from graph object, honouring the expand_aux and (new) expand_msg
97             # flags.
98             sub expansion {
99              
100 0     0 0   my ($self, $bone_or_node) = @_;
101              
102 0           my ($bone, $node);
103              
104 0 0         if (ref($bone_or_node)) {
105 0           $bone = $bone_or_node;
106 0           $node = $bone->[1];
107             } else {
108 0           $node = $bone_or_node;
109 0           $bone = $self->{graph}->{solution}->[$node];
110             }
111              
112             # pull out frequently-used variables (using hash slice)
113 0           my ($expand_aux,$expand_msg) = @{$self}{"expand_aux","expand_msg"};
  0            
114 0           my ($mblocks,$coblocks) = @{$self}{"mblocks","coblocks"};
  0            
115              
116              
117             # Stage 1: collect list of nodes in the expansion, honouring flags
118 0           my ($min, $max) = $bone->knowns_range;
119 0           my $in = [ @{$bone}[$min .. $max] ];
  0            
120 0           my ($out,$expanded,$done) = ([],0,0);
121              
122 0           if (DEBUG) {
123             print "Expander got initial bone " . $bone->pp . "\n";
124             print "It has known range of [$min,$max]\n";
125             print "The values are " . (join ", ", @{$bone}[$min .. $max]) . "\n";
126             print "Expansion: node ${node}'s input list is " . (join " ", @$in) . "\n";
127             }
128              
129 0           until ($done) {
130             # we may need several loops to expand everything since aux blocks
131             # may appear in the expansion of message blocks and vice-versa.
132             # It's possible to do the expansion with just one loop, but the
133             # code is more messy/complicated.
134              
135 0           for my $i (@$in) {
136 0 0 0       if ($expand_msg and $i < $mblocks) {
    0 0        
      0        
137 0           ++$expanded;
138 0           $bone = $self->{solution}->[$i];
139 0           ($min, $max) = $bone->knowns_range;
140 0           push @$out, ($bone->[$min .. $max]);
141             } elsif ($expand_aux and $i >= $mblocks and $i < $coblocks) {
142 0           ++$expanded;
143 0           $bone = $self->{solution}->[$i];
144 0           ($min, $max) = $bone->knowns_range;
145 0           push @$out, ($bone->[$min .. $max]);
146             } else {
147 0           push @$out, $i;
148             }
149             }
150 0 0         $done = 1 unless $expanded;
151             } continue {
152 0           ($in,$out) = ($out,[]);
153 0           $expanded = 0;
154             }
155              
156             # test expansion after stage 1
157 0           if (0) {
158             for my $i (@$in) {
159             if ($expand_aux) {
160             die "raw expanded list had aux blocks after stage 1\n"
161             if $i >= $mblocks and $i < $coblocks;
162             }
163             if ($expand_msg) {
164             die "raw expanded list had msg blocks after stage 1\n"
165             if $i < $mblocks;
166             }
167             }
168             }
169              
170 0           if (DEBUG) {
171             print "Expansion: list after expand_* is " . (join " ", @$in) . "\n";
172             }
173              
174             # Stage 2: sort the list
175 0           my @sorted = sort { $a <=> $b } @$in;
  0            
176              
177             # Stage 3: create output list containing only nodes that appear an
178             # odd number of times
179 0 0         die "expanded list was empty\n" unless @sorted;
180              
181 0           my ($previous, $runlength) = ($sorted[0], 0);
182 0           my @output = ();
183              
184 0           foreach my $i (@sorted, -1) { # -1 is a sentinel
185 0 0         if ($i == $previous) {
186 0           ++$runlength;
187             } else {
188 0 0         push @output, $previous if $runlength & 1;
189 0           $previous = $i;
190 0           $runlength = 1;
191             }
192             }
193              
194             # test expansion after stage 3
195 0           if (0) {
196             for my $i (@output) {
197             if ($expand_aux) {
198             die "raw expanded list had aux blocks after stage 3\n"
199             if $i >= $mblocks and $i < $coblocks;
200             }
201             if ($expand_msg) {
202             die "raw expanded list had msg blocks after stage 3\n"
203             if $i < $mblocks;
204             }
205             }
206             }
207              
208             # Finish: return list
209 0           return @output;
210              
211             }
212              
213             # expand_aux already handled in graph object (DELETEME)
214             sub xor_list {
215 0     0 0   my $self = shift;
216 0           my $i = shift;
217              
218 0           return ($self->{graph}->xor_list($i));
219              
220             # algorithm will no longer return just composite blocks
221              
222              
223 0           my $coblocks = $self->get_coblocks;
224              
225             # the graph object assigns check blocks indexes after the composite
226             # blocks, but the user would prefer to count them from zero:
227              
228 0           my @list = map { $_ - $coblocks } ($self->{graph}->xor_list($i));
  0            
229              
230 0 0         foreach (@list) { die "xor_list: $_ is negative!\n" if $_ < 0; }
  0            
231              
232 0           return @list;
233             }
234              
235             1;
236              
237             __END__