| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Net::OnlineCode::Bones; |
|
2
|
|
|
|
|
|
|
|
|
3
|
2
|
|
|
2
|
|
15182
|
use strict; |
|
|
2
|
|
|
|
|
3
|
|
|
|
2
|
|
|
|
|
63
|
|
|
4
|
2
|
|
|
2
|
|
7
|
use warnings; |
|
|
2
|
|
|
|
|
3
|
|
|
|
2
|
|
|
|
|
52
|
|
|
5
|
|
|
|
|
|
|
|
|
6
|
2
|
|
|
2
|
|
9
|
use vars qw(@ISA @EXPORT_OK %EXPORT_TAGS $VERSION); |
|
|
2
|
|
|
|
|
2
|
|
|
|
2
|
|
|
|
|
2062
|
|
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
$VERSION = '0.04'; |
|
9
|
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
# |
|
11
|
|
|
|
|
|
|
sub new { |
|
12
|
0
|
|
|
0
|
0
|
|
my ($class, $graph, $top, $nodes) = @_; |
|
13
|
0
|
|
|
|
|
|
my $bone = $nodes; |
|
14
|
0
|
|
|
|
|
|
my $unknowns = scalar(@$nodes); |
|
15
|
|
|
|
|
|
|
|
|
16
|
0
|
0
|
|
|
|
|
die "Bones: refusing to create a bone with empty node list\n" |
|
17
|
|
|
|
|
|
|
unless $unknowns; |
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
#print "new bone $top with list @$nodes\n"; |
|
20
|
|
|
|
|
|
|
|
|
21
|
0
|
|
|
|
|
|
unshift @$bone, $unknowns; # count unknowns |
|
22
|
0
|
|
|
|
|
|
push @$bone, $top; # add "top" node to knowns |
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
#print "bone after unshift/push: @$nodes\n"; |
|
25
|
|
|
|
|
|
|
|
|
26
|
0
|
|
|
|
|
|
my $index = 1; |
|
27
|
|
|
|
|
|
|
|
|
28
|
0
|
|
|
|
|
|
while ($index <= $unknowns) { |
|
29
|
0
|
0
|
|
|
|
|
if ($graph->{solution}->[$bone->[$index]]) { |
|
30
|
|
|
|
|
|
|
# print "swapping bone known bone index $index with $unknowns\n"; |
|
31
|
0
|
|
|
|
|
|
@{$bone}[$index,$unknowns] = @{$bone}[$unknowns,$index]; |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
32
|
0
|
|
|
|
|
|
--$unknowns; |
|
33
|
|
|
|
|
|
|
} else { |
|
34
|
|
|
|
|
|
|
# print "bone index $index is not known\n"; |
|
35
|
0
|
|
|
|
|
|
++$index; |
|
36
|
|
|
|
|
|
|
} |
|
37
|
|
|
|
|
|
|
} |
|
38
|
|
|
|
|
|
|
|
|
39
|
0
|
|
|
|
|
|
$bone->[0] = $unknowns; # save updated count |
|
40
|
|
|
|
|
|
|
|
|
41
|
0
|
|
|
|
|
|
bless $bone, $class; |
|
42
|
|
|
|
|
|
|
} |
|
43
|
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
# Throw the caller a bone (ahem) if they want to construct the object |
|
45
|
|
|
|
|
|
|
# themself (useful in GraphDecoder constructor) |
|
46
|
|
|
|
|
|
|
sub bless { |
|
47
|
0
|
|
|
0
|
0
|
|
my ($class, $object) = @_; |
|
48
|
|
|
|
|
|
|
|
|
49
|
0
|
0
|
|
|
|
|
die "Bones: bless is a class method (call with ...::Bones->bless())\n" |
|
50
|
|
|
|
|
|
|
if ref($class); |
|
51
|
|
|
|
|
|
|
|
|
52
|
0
|
0
|
|
|
|
|
die "Net::OnlineCode::Bones::bless can only bless an ARRAY reference\n" |
|
53
|
|
|
|
|
|
|
unless ref($object) eq "ARRAY"; |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
# warn "Bones got ARRAY to bless: " . (join ", ", @$object) . "\n"; |
|
56
|
|
|
|
|
|
|
|
|
57
|
0
|
0
|
0
|
|
|
|
die "Net::OnlineCode::Bones::bless was given an incorrectly constructed array\n" |
|
58
|
|
|
|
|
|
|
if scalar(@$object) == 0 or $object->[0] > scalar(@$object); |
|
59
|
|
|
|
|
|
|
|
|
60
|
0
|
|
|
|
|
|
bless $object, $class; |
|
61
|
|
|
|
|
|
|
} |
|
62
|
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
# "Firm up" a bone by turning an unknown node from the left side of |
|
65
|
|
|
|
|
|
|
# the equation into a known one on the right side |
|
66
|
|
|
|
|
|
|
sub firm { |
|
67
|
0
|
|
|
0
|
0
|
|
my ($bone, $index) = @_; |
|
68
|
0
|
|
|
|
|
|
my $unknowns = $bone->[0]--; |
|
69
|
|
|
|
|
|
|
|
|
70
|
0
|
|
|
|
|
|
@{$bone}[$index,$unknowns] = @{$bone}[$unknowns,$index]; |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
} |
|
72
|
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
# The "top" node is the number of the check or aux block where the |
|
75
|
|
|
|
|
|
|
# bone was first created. It's always the last value of the list |
|
76
|
|
|
|
|
|
|
sub top { |
|
77
|
0
|
|
|
0
|
0
|
|
my $bone = shift; |
|
78
|
|
|
|
|
|
|
|
|
79
|
0
|
|
|
|
|
|
return $bone->[scalar(@$bone)]; |
|
80
|
|
|
|
|
|
|
} |
|
81
|
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
# The "bottom" node will shuffle to the start of the list of unknown |
|
83
|
|
|
|
|
|
|
# blocks (call only when there is just a single unknown left) |
|
84
|
|
|
|
|
|
|
sub bottom { |
|
85
|
0
|
|
|
0
|
0
|
|
my $bone = shift; |
|
86
|
|
|
|
|
|
|
|
|
87
|
0
|
0
|
|
|
|
|
die "Bones: multiple bottom nodes exist\n" if $bone->[0] > 1; |
|
88
|
0
|
|
|
|
|
|
return $bone->[1]; |
|
89
|
|
|
|
|
|
|
} |
|
90
|
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
# how many unknowns on left side? |
|
92
|
|
|
|
|
|
|
sub unknowns { |
|
93
|
0
|
|
|
0
|
0
|
|
my $bone = shift; |
|
94
|
0
|
|
|
|
|
|
return $bone->[0]; |
|
95
|
|
|
|
|
|
|
} |
|
96
|
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
# how many knowns on right side? |
|
98
|
|
|
|
|
|
|
sub knowns { |
|
99
|
0
|
|
|
0
|
0
|
|
my $bone = shift; |
|
100
|
0
|
|
|
|
|
|
return @$bone - $bone->[0]; |
|
101
|
|
|
|
|
|
|
} |
|
102
|
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
# For extracting the actual known or unknown elements, rather than |
|
105
|
|
|
|
|
|
|
# return a list or spliced part of it, return the range of the knowns |
|
106
|
|
|
|
|
|
|
# part of the array for the caller to iterate over. (more efficient) |
|
107
|
|
|
|
|
|
|
# |
|
108
|
|
|
|
|
|
|
# Both the following subs return an inclusive range [first, last] |
|
109
|
|
|
|
|
|
|
# that's suitable for iterating over with for ($first .. $last) |
|
110
|
|
|
|
|
|
|
# |
|
111
|
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
sub knowns_range { |
|
113
|
0
|
|
|
0
|
0
|
|
my $bone = shift; |
|
114
|
0
|
|
|
|
|
|
return ($bone->[0] + 1, scalar(@$bone) - 1); |
|
115
|
|
|
|
|
|
|
} |
|
116
|
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
# unknowns_range can return [1, 0] if there are no unknowns. Beware! |
|
118
|
|
|
|
|
|
|
sub unknowns_range { |
|
119
|
0
|
|
|
0
|
0
|
|
my $bone = shift; |
|
120
|
0
|
|
|
|
|
|
return (1, $bone->[0]); |
|
121
|
|
|
|
|
|
|
} |
|
122
|
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
# The following two routines find a single unknown, shift it to the |
|
124
|
|
|
|
|
|
|
# start of the array and mark all other nodes as known (used in |
|
125
|
|
|
|
|
|
|
# propagation rule). They differ only in whether a node number or a |
|
126
|
|
|
|
|
|
|
# graph are passed in. (modelled on C code) |
|
127
|
|
|
|
|
|
|
sub known_unsolved { |
|
128
|
|
|
|
|
|
|
|
|
129
|
0
|
|
|
0
|
0
|
|
my ($bone, $node) = @_; |
|
130
|
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
# If given a node number, we just scan the list to find it |
|
132
|
|
|
|
|
|
|
|
|
133
|
0
|
|
|
|
|
|
for (1 .. $bone->[0]) { |
|
134
|
0
|
0
|
|
|
|
|
if ($node == $bone->[$_]) { |
|
135
|
0
|
0
|
|
|
|
|
@{$bone}[$_,1] = @{$bone}[1,$_] if $_ != 1; |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
136
|
0
|
|
|
|
|
|
$bone->[0] = 1; |
|
137
|
0
|
|
|
|
|
|
return $bone->[1]; |
|
138
|
|
|
|
|
|
|
} |
|
139
|
|
|
|
|
|
|
} |
|
140
|
0
|
|
|
|
|
|
die "Bones: Didn't find unsolved node $node\n"; |
|
141
|
|
|
|
|
|
|
} |
|
142
|
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
sub unknown_unsolved { |
|
144
|
|
|
|
|
|
|
|
|
145
|
0
|
|
|
0
|
0
|
|
my ($bone, $graph) = @_; |
|
146
|
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
# If given a graph, we look up nodes in it to see if they're solved |
|
148
|
|
|
|
|
|
|
|
|
149
|
0
|
|
|
|
|
|
for (1 .. $bone->[0]) { |
|
150
|
0
|
0
|
|
|
|
|
if (!$graph->{solution}->[$bone->[$_]]) { |
|
151
|
0
|
0
|
|
|
|
|
@{$bone}[$_,1] = @{$bone}[1,$_] if $_ != 1; |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
152
|
0
|
|
|
|
|
|
$bone->[0] = 1; |
|
153
|
0
|
|
|
|
|
|
return $bone->[1]; |
|
154
|
|
|
|
|
|
|
} |
|
155
|
|
|
|
|
|
|
} |
|
156
|
0
|
|
|
|
|
|
die "Bones: Bone has no unsolved nodes\n"; |
|
157
|
|
|
|
|
|
|
} |
|
158
|
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
# We can use the propagation rule from an aux block to a message |
|
161
|
|
|
|
|
|
|
# block, but if the aux block itself is not solved, we end up with two |
|
162
|
|
|
|
|
|
|
# unknown values in the list. This routine takes the aux block number |
|
163
|
|
|
|
|
|
|
# and the single unknown down edge, marks both of them as unknown and |
|
164
|
|
|
|
|
|
|
# the rest as known. |
|
165
|
|
|
|
|
|
|
sub two_unknowns { |
|
166
|
0
|
|
|
0
|
0
|
|
my ($bone, $graph) = @_; |
|
167
|
0
|
|
|
|
|
|
my ($index, $kindex) = (1,1); |
|
168
|
0
|
|
|
|
|
|
my $unknowns = $bone->[0]; |
|
169
|
|
|
|
|
|
|
|
|
170
|
0
|
|
|
|
|
|
print "two_unknowns: Looking for two unsolved in " . $bone->pp . |
|
171
|
|
|
|
|
|
|
" (had $unknowns unknowns)\n"; |
|
172
|
|
|
|
|
|
|
|
|
173
|
0
|
|
|
|
|
|
while ($index <= $unknowns + 1) { |
|
174
|
0
|
|
|
|
|
|
my $node = $bone->[$index]; |
|
175
|
0
|
|
|
|
|
|
print "two_unknowns: Considering node $node at index $index\n"; |
|
176
|
0
|
0
|
|
|
|
|
if ($graph->{solution}->[$node]) { |
|
177
|
0
|
|
|
|
|
|
print "two_unknowns: Node $node is solved; skipping\n"; |
|
178
|
0
|
|
|
|
|
|
--$unknowns; |
|
179
|
|
|
|
|
|
|
} else { |
|
180
|
0
|
|
|
|
|
|
print "two_unknowns: Node $node is unsolved; shuffling to position $kindex\n"; |
|
181
|
0
|
0
|
|
|
|
|
@{$bone}[$index,$kindex] = @{$bone}[$kindex,$index] |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
if $index != $kindex; |
|
183
|
0
|
|
|
|
|
|
++$kindex; |
|
184
|
|
|
|
|
|
|
} |
|
185
|
0
|
|
|
|
|
|
++$index; |
|
186
|
|
|
|
|
|
|
} |
|
187
|
0
|
0
|
|
|
|
|
die "Bones: didn't find two unknowns\n" unless $kindex == 3; |
|
188
|
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
# swap elments if needed so that message node is first |
|
190
|
0
|
0
|
|
|
|
|
@{$bone}[1,2] = @{$bone}[2,1] if $bone->[1] > $bone->[2]; |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
|
|
192
|
0
|
|
|
|
|
|
$bone->[0] = 2; |
|
193
|
|
|
|
|
|
|
|
|
194
|
0
|
|
|
|
|
|
print "two_unknowns: Final contents are " . $bone->pp . "\n"; |
|
195
|
|
|
|
|
|
|
|
|
196
|
0
|
|
|
|
|
|
return $bone->[1]; |
|
197
|
|
|
|
|
|
|
} |
|
198
|
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
# "pretty printer": output in the form "[unknowns] <- [knowns]" |
|
200
|
|
|
|
|
|
|
sub pp { |
|
201
|
|
|
|
|
|
|
|
|
202
|
0
|
|
|
0
|
0
|
|
my $bone = shift; |
|
203
|
0
|
|
|
|
|
|
my ($s, $min, $max) = ("["); |
|
204
|
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
# print "raw bone is ". (join ",", @$bone) . "\n"; |
|
206
|
|
|
|
|
|
|
|
|
207
|
0
|
|
|
|
|
|
($min, $max) = $bone->unknowns_range; |
|
208
|
|
|
|
|
|
|
# print "unknown range: [$min,$max]\n"; |
|
209
|
0
|
0
|
|
|
|
|
$s.= join ", ", map { $bone->[$_] } ($min .. $max) if $min <= $max; |
|
|
0
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
|
|
211
|
0
|
|
|
|
|
|
$s.= "] <- ["; |
|
212
|
|
|
|
|
|
|
|
|
213
|
0
|
|
|
|
|
|
($min, $max) = $bone->knowns_range; |
|
214
|
|
|
|
|
|
|
# print "known range: [$min,$max]\n"; |
|
215
|
0
|
0
|
|
|
|
|
$s.= join ", ", map { $bone->[$_] } ($min .. $max) if $min <= $max; |
|
|
0
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
|
|
217
|
0
|
|
|
|
|
|
return $s . "]"; |
|
218
|
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
} |
|
220
|
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
1; |
|
222
|
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
=head1 NAME |
|
224
|
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
Net::OnlineCode::Bones - Graph decoding internals |
|
226
|
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
228
|
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
This page gives an overview of how the decoding algorithm for Online |
|
230
|
|
|
|
|
|
|
Codes work. |
|
231
|
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
The decoding algorithm can be described in one of two ways: |
|
233
|
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
=over |
|
235
|
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
=item * in terms of solving a set of algebraic equations; and |
|
237
|
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
=item * in terms of resolving a graph. |
|
239
|
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
=back |
|
241
|
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
The first of these explains I the algorithm does, while the |
|
243
|
|
|
|
|
|
|
second describes I it does it. |
|
244
|
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
=head2 Solving a system of algebraic equations |
|
246
|
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
Recall that the Online Codes algorithm works with: |
|
248
|
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
=over |
|
250
|
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
=item * I blocks, which are portions of the original file |
|
252
|
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
=item * I blocks, which are the XOR sum of one or more |
|
254
|
|
|
|
|
|
|
I blocks. |
|
255
|
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
=item * I blocks, which are the XOR sum of one or more |
|
257
|
|
|
|
|
|
|
I and/or I blocks. |
|
258
|
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
=back |
|
260
|
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
On the encoder side, the algorithm generates I blocks by |
|
262
|
|
|
|
|
|
|
using a pseudo-random number generator (PRNG). These blocks are stored |
|
263
|
|
|
|
|
|
|
locally by the encoder, but are never transmitted. However, by sending |
|
264
|
|
|
|
|
|
|
the seed value for the PRNG to the decoder, the decoder knows how the |
|
265
|
|
|
|
|
|
|
auxiliary blocks were constructed, even though it does not know the |
|
266
|
|
|
|
|
|
|
values of them. In other words, give the PRNG seed value, the decoder |
|
267
|
|
|
|
|
|
|
can construct a set of equations, one for each auxiliary block: |
|
268
|
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
aux = msg XOR msg XOR ... |
|
270
|
|
|
|
|
|
|
1 x y ... |
|
271
|
|
|
|
|
|
|
aux = ... |
|
272
|
|
|
|
|
|
|
2 |
|
273
|
|
|
|
|
|
|
: |
|
274
|
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
Initially, all the values in these equations are unknown on the |
|
277
|
|
|
|
|
|
|
decoder side. |
|
278
|
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
As for I blocks, the encoder picks a random seed value for its |
|
280
|
|
|
|
|
|
|
PRNG and uses this to generate a list of message and/or check blocks |
|
281
|
|
|
|
|
|
|
to XOR together to calculate the check block's value. It sends both |
|
282
|
|
|
|
|
|
|
the seed used and the final XOR value to the decoder. As with |
|
283
|
|
|
|
|
|
|
auxiliary blocks, the decoder can use the PRNG with the transmitted |
|
284
|
|
|
|
|
|
|
seed value to construct an equation for a received check block: |
|
285
|
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
chk = msg_or_aux XOR msg_or_aux XOR ... |
|
287
|
|
|
|
|
|
|
1 x y |
|
288
|
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
Unlike the equations constructed for auxiliary blocks, however, the |
|
290
|
|
|
|
|
|
|
value of the check block is also sent to the decoder, so each equation |
|
291
|
|
|
|
|
|
|
includes a single known value on the left-hand side of the |
|
292
|
|
|
|
|
|
|
equation. |
|
293
|
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
Before the first check block is received, the decoder has a set of |
|
295
|
|
|
|
|
|
|
equations involving unknown values. As check blocks are received, |
|
296
|
|
|
|
|
|
|
eventually one of them will be composed of just a single message or |
|
297
|
|
|
|
|
|
|
auxiliary block. In algebraic terms, we have: |
|
298
|
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
chk = msg_or_aux |
|
300
|
|
|
|
|
|
|
x y |
|
301
|
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
Since there is just a single unknown value in the equation, we can |
|
303
|
|
|
|
|
|
|
reverse the order of it and use the new form of the equation |
|
304
|
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
msg_or_aux = chk |
|
306
|
|
|
|
|
|
|
y x |
|
307
|
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
Since we have a single unknown value on the left side and only known |
|
309
|
|
|
|
|
|
|
values on the right side, this new rule solves the value on the left. |
|
310
|
|
|
|
|
|
|
Now wherever this message/aux block appears in another equation, we |
|
311
|
|
|
|
|
|
|
can substitute the right side of the equation. This removes one |
|
312
|
|
|
|
|
|
|
unknown value from the set of equations each time this step is taken. |
|
313
|
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
Decoding progresses in this way by finding an equation with only a |
|
315
|
|
|
|
|
|
|
single unknown value, solving that unknown value then substituting the |
|
316
|
|
|
|
|
|
|
result into any other equation that mentions this value. This proceeds |
|
317
|
|
|
|
|
|
|
until there are no unknowns left. At that point the entire file has |
|
318
|
|
|
|
|
|
|
been "solved". |
|
319
|
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
=head2 Solution in terms of a graph |
|
321
|
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
The method of solving all of the equations above can be re-expressed |
|
323
|
|
|
|
|
|
|
in terms of a graph. Nodes in the graph represent blocks, while the |
|
324
|
|
|
|
|
|
|
edges capture the relation between blocks on the left side of an |
|
325
|
|
|
|
|
|
|
equation and those on the right. So for example, a check block C |
|
326
|
|
|
|
|
|
|
(on the left hand side of an equation) is composed of an auxiliary |
|
327
|
|
|
|
|
|
|
node A and a message node M is represented by: |
|
328
|
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
=over |
|
330
|
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
=item * three nodes M, A and C |
|
332
|
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
=item * an edge between M and C |
|
334
|
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
=item * an edge between A and C |
|
336
|
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
=back |
|
338
|
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
There is also an additional structure imposed on the nodes in the |
|
340
|
|
|
|
|
|
|
graph so that edges can be unambiguously identified as belonging to a |
|
341
|
|
|
|
|
|
|
particular equation. Technically, the graph is a I |
|
342
|
|
|
|
|
|
|
graph. It keeps each of the block types grouped with other blocks of |
|
343
|
|
|
|
|
|
|
that type and orders the groups like so: |
|
344
|
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
message blocks < auxiliary blocks < check blocks |
|
346
|
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
Graphically, the example rule above could be illustrated as follows: |
|
348
|
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
message auxiliary check |
|
350
|
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
M <--------------------------------- C |
|
352
|
|
|
|
|
|
|
/ |
|
353
|
|
|
|
|
|
|
A <------------/ |
|
354
|
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
This diagram could equally have been written with the check blocks on |
|
357
|
|
|
|
|
|
|
the left and the message blocks on the right, or turned 90 |
|
358
|
|
|
|
|
|
|
degrees. It's merely a matter of convention, similar to the two ways |
|
359
|
|
|
|
|
|
|
of writing out the equation as either: |
|
360
|
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
C <- M xor A |
|
362
|
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
or |
|
364
|
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
M xor A -> C |
|
366
|
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
For the remainder of the document, I'll go with the convention of |
|
368
|
|
|
|
|
|
|
saying that auxiliary blocks are to the right of the message blocks |
|
369
|
|
|
|
|
|
|
and check blocks are to the right of both of them. (My code uses a |
|
370
|
|
|
|
|
|
|
different convention again and talks about check nodes being higher |
|
371
|
|
|
|
|
|
|
than auxiliary and message nodes). |
|
372
|
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
Besides information about edges, the graph also stores a status bit |
|
374
|
|
|
|
|
|
|
for each node to indicate whether that node is known (solved) or |
|
375
|
|
|
|
|
|
|
unknown. Check nodes are always taken to be solved since the encoder |
|
376
|
|
|
|
|
|
|
sends the value of that block, whereas message and auxiliary nodes are |
|
377
|
|
|
|
|
|
|
all initially unknown/unsolved. |
|
378
|
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
In the explanation of the algebraic interpretation, I talked about |
|
380
|
|
|
|
|
|
|
finding an equation that had just a single unknown and rearranging it |
|
381
|
|
|
|
|
|
|
so that the single unknown value moves to one side and all the other |
|
382
|
|
|
|
|
|
|
knowns move to the other side. There is an analoguous operation on the |
|
383
|
|
|
|
|
|
|
graph, and this is named the "propagation rule". |
|
384
|
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
The propagation rule involves finding a known node which has exactly |
|
386
|
|
|
|
|
|
|
one unsolved neighbour on the left. In the above example, if we are |
|
387
|
|
|
|
|
|
|
considering whether to propagate from node C (which is known) both M |
|
388
|
|
|
|
|
|
|
and A are unknown, so the rule does not match. If, on the other hand, |
|
389
|
|
|
|
|
|
|
one of M or A are known, the rule does match. |
|
390
|
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
When the propagation rule matches, the solution for the newly-solved |
|
392
|
|
|
|
|
|
|
node on the left becomes the XOR of the node on the right plus all the |
|
393
|
|
|
|
|
|
|
other nodes emanating from that (right) node. When a node is solved in |
|
394
|
|
|
|
|
|
|
this way, all edges from the node on the right are removed from the |
|
395
|
|
|
|
|
|
|
graph. |
|
396
|
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
In my code, the propagation rule is handled in a routine called |
|
398
|
|
|
|
|
|
|
resolve(). |
|
399
|
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
=head2 Cascades |
|
401
|
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
When matched, the propagation rule solves one extra node somewhere to |
|
403
|
|
|
|
|
|
|
the left of the starting node. In the algebraic interpretation, I |
|
404
|
|
|
|
|
|
|
talked about substituting a newly-solved variable into all other |
|
405
|
|
|
|
|
|
|
equations where the variable appeared. There is an analogous procedure |
|
406
|
|
|
|
|
|
|
in the graph-based implementation, which is implemenented in the |
|
407
|
|
|
|
|
|
|
cascade() routine. |
|
408
|
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
For the sake of discussion, let's assume that the message block M was |
|
410
|
|
|
|
|
|
|
solved by the propagation rule and that it had the solution: |
|
411
|
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
M <- A xor C |
|
413
|
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
To simulate substituting M into all other equations where it appears, |
|
415
|
|
|
|
|
|
|
we need to work backwards (from left to right) from node M and see if |
|
416
|
|
|
|
|
|
|
any of those nodes now match the propagation rule. Since there will be |
|
417
|
|
|
|
|
|
|
one rightward edge in the graph from that node for each equation the |
|
418
|
|
|
|
|
|
|
left node appears in, this effectively reaches all equations that |
|
419
|
|
|
|
|
|
|
could could become solvable. |
|
420
|
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
In the case where the left node which has become solved is an |
|
422
|
|
|
|
|
|
|
auxiliary block, the cascade() routine also queues up the auxiliary |
|
423
|
|
|
|
|
|
|
node itself for checking the propagation rule. |
|
424
|
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
=head2 Special handling for auxiliary nodes |
|
426
|
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
Although in theory the propagation rule could be applied to unsolved |
|
428
|
|
|
|
|
|
|
auxiliary nodes, in practice this has proved troublesome, so I have |
|
429
|
|
|
|
|
|
|
not implemented it. Instead I have implemented a special "auxiliary |
|
430
|
|
|
|
|
|
|
rule" that gives comparable results. |
|
431
|
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
Recall that the propagation rule works with a single known node on the |
|
433
|
|
|
|
|
|
|
right and a single unknown node on the left. It is also possible to |
|
434
|
|
|
|
|
|
|
devise a rule where there is a message node on the left and an |
|
435
|
|
|
|
|
|
|
unsolved aux rule on the right. If the auxiliary node has only one |
|
436
|
|
|
|
|
|
|
unsolved left neighbour (ie the message node) and that message node |
|
437
|
|
|
|
|
|
|
becomes solved, then the auxiliary block can be solved too. |
|
438
|
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
Initially, each auxiliary block will be composed of some number of |
|
440
|
|
|
|
|
|
|
message blocks: |
|
441
|
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
aux = msg xor msg xor ... |
|
443
|
|
|
|
|
|
|
x i j |
|
444
|
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
When the last unsolved message block on the right becomes solved then |
|
446
|
|
|
|
|
|
|
this equation has no more unknowns apart from the aux block |
|
447
|
|
|
|
|
|
|
itself. Therefore, it can be marked as solved (with the above |
|
448
|
|
|
|
|
|
|
solution) and we can cascade up from that aux block to see if it |
|
449
|
|
|
|
|
|
|
solves any more equations. |
|
450
|
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
=head2 Optimising by tracking counts of unsolved left nodes |
|
452
|
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
When aux or check nodes are created, the number of unknown/unsolved |
|
454
|
|
|
|
|
|
|
edges that they are comprised of is calculated. Whenever a node |
|
455
|
|
|
|
|
|
|
becomes solved, each of the nodes that include that node in its list |
|
456
|
|
|
|
|
|
|
of edges has their unsolved count decremented. |
|
457
|
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
This improves performance by avoiding having to scan the node's full |
|
459
|
|
|
|
|
|
|
list of leftward edges when it is considered for resolving. |
|
460
|
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
=head2 "Bones" ("Bundles of Node Elements") |
|
462
|
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
In a previous version of the program, edges in the graph were stored |
|
464
|
|
|
|
|
|
|
by keeping track of the left (bottom) end of the edge in a hash, while |
|
465
|
|
|
|
|
|
|
the right (top) end was stored in a list. I also had a separate array |
|
466
|
|
|
|
|
|
|
for storing the solutions of each node. The "Bones" structure |
|
467
|
|
|
|
|
|
|
essentially combines the top part of the edge and the solution into a |
|
468
|
|
|
|
|
|
|
single fixed-size array. This was done to improve performance by |
|
469
|
|
|
|
|
|
|
eliminating lots of list copying as the graph was processed. |
|
470
|
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
The Bones structure is a fixed-sized array with three elements: |
|
472
|
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
=over |
|
474
|
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
=item * count of unsolved left (down) edges |
|
476
|
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
=item * node ids of unsolved left (down) edges |
|
478
|
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
=item * list of known node ids |
|
480
|
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
=back |
|
482
|
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
Bones can also be viewed as encapsulating an algebraic equation of the |
|
484
|
|
|
|
|
|
|
form: |
|
485
|
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
[unknown nodes] <- [known nodes] |
|
487
|
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
At the start of decoding, each auxiliary node has a Bone object |
|
489
|
|
|
|
|
|
|
created for it: |
|
490
|
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
[aux node, message nodes] <- [] |
|
492
|
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
That is, the aux node and all its constituent message nodes are all |
|
494
|
|
|
|
|
|
|
marked as unknown/unsolved (there are no knowns in the equation). |
|
495
|
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
The "Bone" is attached to the auxiliary node and reciprocal links (the |
|
497
|
|
|
|
|
|
|
other end of the edges) are created in each of the component message |
|
498
|
|
|
|
|
|
|
nodes. All the nodes in the left hand side except the aux node itself |
|
499
|
|
|
|
|
|
|
are considered to be the top parts of edges. |
|
500
|
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
When a check node is created, its Bone is of the form: |
|
502
|
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
[unsolved msg/aux nodes] <- [ check node, solved msg/aux nodes ] |
|
504
|
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
The check node is placed on the right along with other known nodes |
|
506
|
|
|
|
|
|
|
because the decoder knows the value of all received check nodes. |
|
507
|
|
|
|
|
|
|
Reciprocal links are only created for unsolved nodes. |
|
508
|
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
As can be seen, Bones have aspects of algebraic equations, but they |
|
510
|
|
|
|
|
|
|
also encapsulate edge structure. |
|
511
|
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
As nodes become solved by the propagation or auxiliary rule, elements |
|
513
|
|
|
|
|
|
|
are shifted in the array to take this form: |
|
514
|
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
[newly-solved node] <- [list of nodes to XOR to get value] |
|
516
|
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
This is exactly the form of a solution to a node, so the Bone is |
|
518
|
|
|
|
|
|
|
stored in the solution array. The right-hand side is also scanned and |
|
519
|
|
|
|
|
|
|
any reciprocal links are deleted, as is the top part of the edge. |
|
520
|
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
In summary, a Bone always represents an equation. At the start of the |
|
522
|
|
|
|
|
|
|
decoding process it also encapsulates edge structure, but at the end |
|
523
|
|
|
|
|
|
|
it becomes a solution for either a message block or an auxiliary block. |
|
524
|
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
From version 0.04 of the Net::OnlineCode modules onwards, the resolver |
|
526
|
|
|
|
|
|
|
returns a Bone object for each solved node. It will always be an array |
|
527
|
|
|
|
|
|
|
of the form mentioned above, and encoded as follows: |
|
528
|
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
[ |
|
530
|
|
|
|
|
|
|
1, # the number of "unknowns" |
|
531
|
|
|
|
|
|
|
msg_or_aux, # the node that was just solved |
|
532
|
|
|
|
|
|
|
list of component nodes |
|
533
|
|
|
|
|
|
|
] |
|
534
|
|
|
|
|
|
|
|