File Coverage

blib/lib/Math/ReedSolomon/Encoder.pm
Criterion Covered Total %
statement 95 95 100.0
branch 5 8 62.5
condition 1 3 33.3
subroutine 14 14 100.0
pod 4 4 100.0
total 119 124 95.9


line stmt bran cond sub pod time code
1             # Liberally adapted from:
2             # https://en.wikiversity.org/wiki/Reed%E2%80%93Solomon_codes_for_coders
3              
4             package Math::ReedSolomon::Encoder;
5 2     2   429398 use v5.24;
  2         10  
6 2     2   11 use warnings;
  2         3  
  2         142  
7 2     2   1003 use experimental qw< signatures >;
  2         6039  
  2         8  
8             { our $VERSION = '0.001' }
9              
10 2     2   389 use Exporter qw< import >;
  2         3  
  2         2745  
11             our @EXPORT_OK = qw<
12             rs_correction
13             rs_correction_string
14             rs_encode
15             rs_encode_string
16             >;
17             our %EXPORT_TAGS = (all => [ @EXPORT_OK ]);
18              
19             our $ALPHA = 2;
20             our $PRIME_POLY = 0X11D;
21              
22             ########################################################################
23             #
24             # Public Interface
25              
26 8     8 1 132078 sub rs_correction ($msg, $nsym) {
  8         10  
  8         8  
  8         9  
27 8         15 my $g = _rs_generator_poly($nsym);
28 8         24 my ($quot, $rem) = _gf256_poly_div([$msg->@*, (0) x $nsym ], $g);
29 8         31 return $rem;
30             }
31              
32 4     4 1 6 sub rs_correction_string ($msg, $nsym) {
  4         6  
  4         4  
  4         5  
33 4         13 my $aref = [ map { ord($_) } split m{}mxs, $msg ];
  40         43  
34 4         11 return join '', map { chr($_) } rs_correction($aref, $nsym)->@*;
  26         53  
35             }
36              
37 2     2 1 3 sub rs_encode ($msg, $nsym) {
  2         4  
  2         2  
  2         2  
38 2         4 return [ $msg->@*, rs_correction($msg, $nsym)->@* ];
39             }
40              
41 2     2 1 3 sub rs_encode_string ($msg, $nsym) {
  2         4  
  2         2  
  2         2  
42 2         5 return $msg . rs_correction_string($msg, $nsym);
43             }
44              
45              
46             ########################################################################
47             #
48             # Private Interface
49              
50 8     8   8 sub _rs_generator_poly ($nsym) {
  8         9  
  8         9  
51 8         8 state $gs = [ [1] ];
52 8         24 push $gs->@*, _gf256_poly_mul($gs->[-1], [1, _gf256_pow($ALPHA, $gs->$#*)])
53             while $nsym > $gs->$#*;
54 8         13 return $gs->[$nsym];
55             }
56              
57             sub _gf256_table_for {
58 2     2   3 state $table_for = do {
59 1         2 my (@exp, @log);
60 1         2 my $x = 1;
61 1         3 for my $i (0 .. 254) {
62 255         318 $exp[$i] = $exp[$i + 255] = $x;
63 255         272 $log[$x] = $i;
64 255         262 $x <<= 1;
65 255 100       321 $x ^= $PRIME_POLY if $x & 0x100;
66             }
67 1         7 { exp => \@exp, log => \@log };
68             };
69             }
70              
71 798     798   710 sub _gf256_mul ($x, $y) {
  798         733  
  798         716  
  798         660  
72 798         696 state $table_for = _gf256_table_for();
73 798         713 state $exp = $table_for->{exp};
74 798         668 state $log = $table_for->{log};
75 798 50 33     1335 return 0 if $x == 0 || $y == 0;
76 798         1062 return $exp->[$log->[$x] + $log->[$y]];
77             }
78              
79 10     10   10 sub _gf256_pow ($x, $pow) {
  10         11  
  10         12  
  10         10  
80 10         11 state $table_for = _gf256_table_for();
81 10         16 state $exp = $table_for->{exp};
82 10         9 state $log = $table_for->{log};
83 10         19 return $exp->[($log->[$x] * $pow) % 255];
84             }
85              
86 10     10   11 sub _gf256_poly_mul ($p, $q) {
  10         8  
  10         15  
  10         9  
87 10         9 my $lp = $p->@*;
88 10         9 my $lq = $q->@*;
89 10         11 my $lr = $lp + $lq - 1;
90 10         16 my $r = [ (0) x $lr ];
91 10         15 for my $i (0 .. ($lp - 1)) {
92 55         60 for my $j (0 .. ($lq - 1)) {
93 110         134 $r->[$i + $j] ^= _gf256_mul($p->[$i], $q->[$j]);
94             }
95             }
96 10         30 return $r;
97             }
98              
99 8     8   11 sub _gf256_poly_div ($x, $y) {
  8         7  
  8         8  
  8         5  
100 8         14 my $retval = [ $x->@* ];
101 8         18 for my $i (0 .. ($x->$#* - $y->$#*)) {
102 80         92 my $c = $retval->[$i];
103 80 50       113 if ($c != 0) {
104 80         100 for my $j (1 .. $y->$#*) {
105 688 50       801 if ($y->[$j] != 0) {
106 688         780 $retval->[$i + $j] ^= _gf256_mul($y->[$j], $c);
107             }
108             }
109             }
110             }
111 8         12 my $separator = $retval->$#* - $y->$#*;
112 8         55 my $quot = [ $retval->@[0 .. $separator] ];
113 8         17 my $rem = [ $retval->@[$separator + 1 .. $retval->$#*] ];
114 8         23 return ($quot, $rem);
115             }
116              
117             1;