1 module ithox.qrcode.common.reedsolomoncodec;
2 import std.conv;
3 
4 /**
5 * Reed-Solomon codec for 8-bit characters.
6 *
7 * Based on libfec by Phil Karn, KA9Q.
8 */
9 class ReedSolomonCodec
10 {
11 
12     protected int symbolSize, blockSize, padding, firstRoot, primitive, numRoots, iPrimitive;
13 
14     protected int[] alphaTo, _indexOf, generatorPoly;
15     /**
16 	* Creates a new reed solomon instance.
17 	*
18 	* @param  integer symbolSize
19 	* @param  integer gfPoly
20 	* @param  integer firstRoot
21 	* @param  integer primitive
22 	* @param  integer numRoots
23 	* @param  integer padding
24 	* @throws Exception\InvalidArgumentException
25 	* @throws Exception\RuntimeException
26 	*/
27     public this(int symbolSize_, int gfPoly_, int firstRoot_, int primitive_,
28             int numRoots_, int padding_)
29     {
30         if (symbolSize_ < 0 || symbolSize_ > 8)
31         {
32             throw new Exception("Symbol size must be between 0 and 8");
33         }
34         if (firstRoot_ < 0 || firstRoot_ >= (1 << symbolSize_))
35         {
36             throw new Exception("First root must be between 0 and " ~ to!string((1 << symbolSize_)));
37         }
38         if (numRoots_ < 0 || numRoots_ >= (1 << symbolSize_))
39         {
40             throw new Exception("Num roots must be between 0 and " ~ to!string((1 << symbolSize_)));
41         }
42         if (padding_ < 0 || padding_ >= ((1 << symbolSize_) - 1 - numRoots_))
43         {
44             throw new Exception("Padding must be between 0 and " ~ to!string(
45                     ((1 << symbolSize_) - 1 - numRoots_)));
46         }
47         this.symbolSize = symbolSize_;
48         this.blockSize = (1 << symbolSize_) - 1;
49         this.padding = padding_;
50         this.alphaTo = new int[this.blockSize + 1];
51         this.alphaTo[] = 0;
52         this._indexOf = new int[this.blockSize + 1];
53         this._indexOf[] = 0;
54 
55         // Generate galous field lookup table
56         this._indexOf[0] = this.blockSize;
57         this.alphaTo[this.blockSize] = 0;
58         auto sr = 1;
59         for (auto i = 0; i < this.blockSize; i++)
60         {
61             this._indexOf[sr] = i;
62             this.alphaTo[i] = sr;
63             sr <<= 1;
64             if (sr & (1 << symbolSize_))
65             {
66                 sr ^= gfPoly_;
67             }
68             sr &= this.blockSize;
69         }
70         if (sr != 1)
71         {
72             throw new Exception("Field generator polynomial is not primitive");
73         }
74         // Form RS code generator polynomial from its roots
75         this.generatorPoly = new int[numRoots_ + 1];
76         this.generatorPoly[] = 0;
77 
78         this.firstRoot = firstRoot_;
79         this.primitive = primitive_;
80         this.numRoots = numRoots_;
81         // Find prim-th root of 1, used in decoding
82         auto _iPrimitive = 1;
83         for (_iPrimitive = 1; (_iPrimitive % primitive) != 0; _iPrimitive += this.blockSize)
84         {
85         }
86         this.iPrimitive = to!int(iPrimitive / primitive);
87         this.generatorPoly[0] = 1;
88         for (auto i = 0, root = firstRoot * primitive_; i < numRoots_; i++, root += primitive_)
89         {
90             this.generatorPoly[i + 1] = 1;
91             int j = 0;
92             for (j = i; j > 0; j--)
93             {
94                 if (this.generatorPoly[j] != 0)
95                 {
96                     this.generatorPoly[j] = this.generatorPoly[j - 1] ^ this.alphaTo[this.modNn(
97                             this._indexOf[this.generatorPoly[j]] + root)];
98                 }
99                 else
100                 {
101                     this.generatorPoly[j] = this.generatorPoly[j - 1];
102                 }
103             }
104             this.generatorPoly[j] = this.alphaTo[this.modNn(
105                     this._indexOf[this.generatorPoly[0]] + root)];
106         }
107         // Convert generator poly to index form for quicker encoding
108         for (auto i = 0; i <= numRoots_; i++)
109         {
110             this.generatorPoly[i] = this._indexOf[this.generatorPoly[i]];
111         }
112     }
113 
114     /**
115 	* Computes x % GF_SIZE, where GF_SIZE is 2**GF_BITS - 1, without a slow
116 	* divide.
117 	*
118 	* @param  itneger x
119 	* @return integer
120 	*/
121     protected int modNn(int x)
122     {
123         while (x >= this.blockSize)
124         {
125             x -= this.blockSize;
126             x = (x >> this.symbolSize) + (x & this.blockSize);
127         }
128         return x;
129     }
130 
131     /**
132 	* Encodes data and writes result back into parity array.
133 	*
134 	* @param  SplFixedArray data
135 	* @param  SplFixedArray parity
136 	* @return void
137 	*/
138     public void encode(int[] data, int[] parity)
139     {
140         for (auto i = 0; i < this.numRoots; i++)
141         {
142             parity[i] = 0;
143         }
144         auto iterations = this.blockSize - this.numRoots - this.padding;
145         for (auto i = 0; i < iterations; i++)
146         {
147             auto feedback = this._indexOf[data[i] ^ parity[0]];
148             if (feedback != this.blockSize)
149             {
150                 // Feedback term is non-zero
151                 feedback = this.modNn(this.blockSize - this.generatorPoly[this.numRoots] + feedback);
152                 for (auto j = 1; j < this.numRoots; j++)
153                 {
154                     parity[j] = parity[j] ^ this.alphaTo[this.modNn(
155                             feedback + this.generatorPoly[this.numRoots - j])];
156                 }
157             }
158             for (auto j = 0; j < this.numRoots - 1; j++)
159             {
160                 parity[j] = parity[j + 1];
161             }
162             if (feedback != this.blockSize)
163             {
164                 parity[this.numRoots - 1] = this.alphaTo[this.modNn(
165                         feedback + this.generatorPoly[0])];
166             }
167             else
168             {
169                 parity[this.numRoots - 1] = 0;
170             }
171         }
172     }
173 
174 }