reed_solomon_erasure/
galois_16.rs

1//! GF(2^16) implementation.
2//!
3//! More accurately, this is a `GF((2^8)^2)` implementation which builds an extension
4//! field of `GF(2^8)`, as defined in the `galois_8` module.
5
6use crate::galois_8;
7use core::ops::{Add, Div, Mul, Sub};
8
9// the irreducible polynomial used as a modulus for the field.
10// print R.irreducible_element(2,algorithm="first_lexicographic" )
11// x^2 + a*x + a^7
12//
13// hopefully it is a fast polynomial
14const EXT_POLY: [u8; 3] = [1, 2, 128];
15
16/// The field GF(2^16).
17#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
18pub struct Field;
19
20impl crate::Field for Field {
21    const ORDER: usize = 65536;
22
23    type Elem = [u8; 2];
24
25    fn add(a: [u8; 2], b: [u8; 2]) -> [u8; 2] {
26        (Element(a) + Element(b)).0
27    }
28
29    fn mul(a: [u8; 2], b: [u8; 2]) -> [u8; 2] {
30        (Element(a) * Element(b)).0
31    }
32
33    fn div(a: [u8; 2], b: [u8; 2]) -> [u8; 2] {
34        (Element(a) / Element(b)).0
35    }
36
37    fn exp(elem: [u8; 2], n: usize) -> [u8; 2] {
38        Element(elem).exp(n).0
39    }
40
41    fn zero() -> [u8; 2] {
42        [0; 2]
43    }
44
45    fn one() -> [u8; 2] {
46        [0, 1]
47    }
48
49    fn nth_internal(n: usize) -> [u8; 2] {
50        [(n >> 8) as u8, n as u8]
51    }
52}
53
54/// Type alias of ReedSolomon over GF(2^8).
55pub type ReedSolomon = crate::ReedSolomon<Field>;
56
57/// Type alias of ShardByShard over GF(2^8).
58pub type ShardByShard<'a> = crate::ShardByShard<'a, Field>;
59
60/// An element of `GF(2^16)`.
61#[derive(Debug, Copy, Clone, PartialEq, Eq)]
62struct Element(pub [u8; 2]);
63
64impl Element {
65    // Create the zero element.
66    fn zero() -> Self {
67        Element([0, 0])
68    }
69
70    // A constant element evaluating to `n`.
71    fn constant(n: u8) -> Element {
72        Element([0, n])
73    }
74
75    // Whether this is the zero element.
76    fn is_zero(&self) -> bool {
77        self.0 == [0; 2]
78    }
79
80    fn exp(mut self, n: usize) -> Element {
81        if n == 0 {
82            Element::constant(1)
83        } else if self == Element::zero() {
84            Element::zero()
85        } else {
86            let x = self;
87            for _ in 1..n {
88                self = self * x;
89            }
90
91            self
92        }
93    }
94
95    // reduces from some polynomial with degree <= 2.
96    #[inline]
97    fn reduce_from(mut x: [u8; 3]) -> Self {
98        if x[0] != 0 {
99            // divide x by EXT_POLY and use remainder.
100            // i = 0 here.
101            // c*x^(i+j)  = a*x^i*b*x^j
102            x[1] ^= galois_8::mul(EXT_POLY[1], x[0]);
103            x[2] ^= galois_8::mul(EXT_POLY[2], x[0]);
104        }
105
106        Element([x[1], x[2]])
107    }
108
109    fn degree(&self) -> usize {
110        if self.0[0] != 0 {
111            1
112        } else {
113            0
114        }
115    }
116}
117
118impl From<[u8; 2]> for Element {
119    fn from(c: [u8; 2]) -> Self {
120        Element(c)
121    }
122}
123
124impl Default for Element {
125    fn default() -> Self {
126        Element::zero()
127    }
128}
129
130impl Add for Element {
131    type Output = Element;
132
133    fn add(self, other: Self) -> Element {
134        Element([self.0[0] ^ other.0[0], self.0[1] ^ other.0[1]])
135    }
136}
137
138impl Sub for Element {
139    type Output = Element;
140
141    fn sub(self, other: Self) -> Element {
142        self.add(other)
143    }
144}
145
146impl Mul for Element {
147    type Output = Element;
148
149    fn mul(self, rhs: Self) -> Element {
150        // FOIL; our elements are linear at most, with two coefficients
151        let out: [u8; 3] = [
152            galois_8::mul(self.0[0], rhs.0[0]),
153            galois_8::add(
154                galois_8::mul(self.0[1], rhs.0[0]),
155                galois_8::mul(self.0[0], rhs.0[1]),
156            ),
157            galois_8::mul(self.0[1], rhs.0[1]),
158        ];
159
160        Element::reduce_from(out)
161    }
162}
163
164impl Mul<u8> for Element {
165    type Output = Element;
166
167    fn mul(self, rhs: u8) -> Element {
168        Element([galois_8::mul(rhs, self.0[0]), galois_8::mul(rhs, self.0[1])])
169    }
170}
171
172impl Div for Element {
173    type Output = Element;
174
175    fn div(self, rhs: Self) -> Element {
176        self * rhs.inverse()
177    }
178}
179
180// helpers for division.
181
182#[derive(Debug)]
183enum EgcdRhs {
184    Element(Element),
185    ExtPoly,
186}
187
188impl Element {
189    // compute extended euclidean algorithm against an element of self,
190    // where the GCD is known to be constant.
191    fn const_egcd(self, rhs: EgcdRhs) -> (u8, Element, Element) {
192        if self.is_zero() {
193            let rhs = match rhs {
194                EgcdRhs::Element(elem) => elem,
195                EgcdRhs::ExtPoly => panic!("const_egcd invoked with divisible"),
196            };
197            (rhs.0[1], Element::constant(0), Element::constant(1))
198        } else {
199            let (cur_quotient, cur_remainder) = match rhs {
200                EgcdRhs::Element(rhs) => rhs.polynom_div(self),
201                EgcdRhs::ExtPoly => Element::div_ext_by(self),
202            };
203
204            // GCD is constant because EXT_POLY is irreducible
205            let (g, x, y) = cur_remainder.const_egcd(EgcdRhs::Element(self));
206            (g, y + (cur_quotient * x), x)
207        }
208    }
209
210    // divide EXT_POLY by self.
211    fn div_ext_by(rhs: Self) -> (Element, Element) {
212        if rhs.degree() == 0 {
213            // dividing by constant is the same as multiplying by another constant.
214            // and all constant multiples of EXT_POLY are in the equivalence class
215            // of 0.
216            return (Element::zero(), Element::zero());
217        }
218
219        // divisor is ensured linear here.
220        // now ensure divisor is monic.
221        let leading_mul_inv = galois_8::div(1, rhs.0[0]);
222
223        let monictized = rhs * leading_mul_inv;
224        let mut poly = EXT_POLY;
225
226        for i in 0..2 {
227            let coef = poly[i];
228            for j in 1..2 {
229                if rhs.0[j] != 0 {
230                    poly[i + j] ^= galois_8::mul(monictized.0[j], coef);
231                }
232            }
233        }
234
235        let remainder = Element::constant(poly[2]);
236        let quotient = Element([poly[0], poly[1]]) * leading_mul_inv;
237
238        (quotient, remainder)
239    }
240
241    fn polynom_div(self, rhs: Self) -> (Element, Element) {
242        let divisor_degree = rhs.degree();
243        if rhs.is_zero() {
244            panic!("divide by 0");
245        } else if self.degree() < divisor_degree {
246            // If divisor's degree (len-1) is bigger, all dividend is a remainder
247            (Element::zero(), self)
248        } else if divisor_degree == 0 {
249            // divide by constant.
250            let invert = galois_8::div(1, rhs.0[1]);
251            let quotient = Element([
252                galois_8::mul(invert, self.0[0]),
253                galois_8::mul(invert, self.0[1]),
254            ]);
255
256            (quotient, Element::zero())
257        } else {
258            // self degree is at least divisor degree, divisor degree not 0.
259            // therefore both are 1.
260            debug_assert_eq!(self.degree(), divisor_degree);
261            debug_assert_eq!(self.degree(), 1);
262
263            // ensure rhs is constant.
264            let leading_mul_inv = galois_8::div(1, rhs.0[0]);
265            let monic = Element([
266                galois_8::mul(leading_mul_inv, rhs.0[0]),
267                galois_8::mul(leading_mul_inv, rhs.0[1]),
268            ]);
269
270            let leading_coeff = self.0[0];
271            let mut remainder = self.0[1];
272
273            if monic.0[1] != 0 {
274                remainder ^= galois_8::mul(monic.0[1], self.0[0]);
275            }
276
277            (
278                Element::constant(galois_8::mul(leading_mul_inv, leading_coeff)),
279                Element::constant(remainder),
280            )
281        }
282    }
283
284    /// Convert the inverse of this field element. Panics if zero.
285    fn inverse(self) -> Element {
286        if self.is_zero() {
287            panic!("Cannot invert 0");
288        }
289
290        // first step of extended euclidean algorithm.
291        // done here because EXT_POLY is outside the scope of `Element`.
292        let (gcd, y) = {
293            // self / EXT_POLY = (0, self)
294            let remainder = self;
295
296            // GCD is constant because EXT_POLY is irreducible
297            let (g, x, _) = remainder.const_egcd(EgcdRhs::ExtPoly);
298
299            (g, x)
300        };
301
302        // we still need to normalize it by dividing by the gcd
303        if gcd != 0 {
304            // EXT_POLY is irreducible so the GCD will always be constant.
305            // EXT_POLY*x + self*y = gcd
306            // self*y = gcd - EXT_POLY*x
307            //
308            // EXT_POLY*x is representative of the equivalence class of 0.
309            let normalizer = galois_8::div(1, gcd);
310            y * normalizer
311        } else {
312            // self is equivalent to zero.
313            panic!("Cannot invert 0");
314        }
315    }
316}
317
318#[cfg(test)]
319mod tests {
320    use super::*;
321    use quickcheck::Arbitrary;
322
323    impl Arbitrary for Element {
324        fn arbitrary<G: quickcheck::Gen>(gen: &mut G) -> Self {
325            let a = u8::arbitrary(gen);
326            let b = u8::arbitrary(gen);
327
328            Element([a, b])
329        }
330    }
331
332    quickcheck! {
333        fn qc_add_associativity(a: Element, b: Element, c: Element) -> bool {
334            a + (b + c) == (a + b) + c
335        }
336
337        fn qc_mul_associativity(a: Element, b: Element, c: Element) -> bool {
338            a * (b * c) == (a * b) * c
339        }
340
341        fn qc_additive_identity(a: Element) -> bool {
342            let zero = Element::zero();
343            a - (zero - a) == zero
344        }
345
346        fn qc_multiplicative_identity(a: Element) -> bool {
347            a.is_zero() || {
348                let one = Element([0, 1]);
349                (one / a) * a == one
350            }
351        }
352
353        fn qc_add_commutativity(a: Element, b: Element) -> bool {
354            a + b == b + a
355        }
356
357        fn qc_mul_commutativity(a: Element, b: Element) -> bool {
358            a * b == b * a
359        }
360
361        fn qc_add_distributivity(a: Element, b: Element, c: Element) -> bool {
362            a * (b + c) == (a * b) + (a * c)
363        }
364
365        fn qc_inverse(a: Element) -> bool {
366            a.is_zero() || {
367                let inv = a.inverse();
368                a * inv == Element::constant(1)
369            }
370        }
371
372        fn qc_exponent_1(a: Element, n: u8) -> bool {
373            a.is_zero() || n == 0 || {
374                let mut b = a.exp(n as usize);
375                for _ in 1..n {
376                    b = b / a;
377                }
378
379                a == b
380            }
381        }
382
383        fn qc_exponent_2(a: Element, n: u8) -> bool {
384            a.is_zero() || {
385                let mut res = true;
386                let mut b = Element::constant(1);
387
388                for i in 0..n {
389                    res = res && b == a.exp(i as usize);
390                    b = b * a;
391                }
392
393                res
394            }
395        }
396
397        fn qc_exp_zero_is_one(a: Element) -> bool {
398            a.exp(0) == Element::constant(1)
399        }
400    }
401
402    #[test]
403    #[should_panic]
404    fn test_div_b_is_0() {
405        let _ = Element([1, 0]) / Element::zero();
406    }
407
408    #[test]
409    fn zero_to_zero_is_one() {
410        assert_eq!(Element::zero().exp(0), Element::constant(1))
411    }
412}