ldpc_toolbox/
gf2.rs

1//! Finite field GF(2) arithmetic.
2//!
3//! This module contains the struct [GF2], which implements the finite field
4//! arithmetic in GF(2).
5
6use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
7use ndarray::ScalarOperand;
8use num_traits::{One, Zero};
9
10/// Finite field GF(2) element.
11///
12/// This struct represents an element of the finite field GF(2).
13#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
14pub struct GF2(u8);
15
16impl Zero for GF2 {
17    fn zero() -> GF2 {
18        GF2(0)
19    }
20
21    fn is_zero(&self) -> bool {
22        *self == Self::zero()
23    }
24
25    fn set_zero(&mut self) {
26        *self = Self::zero()
27    }
28}
29
30impl One for GF2 {
31    fn one() -> GF2 {
32        GF2(1)
33    }
34
35    fn set_one(&mut self) {
36        *self = Self::one()
37    }
38
39    fn is_one(&self) -> bool {
40        *self == Self::one()
41    }
42}
43
44impl Add for GF2 {
45    type Output = GF2;
46
47    #[allow(clippy::suspicious_arithmetic_impl)]
48    fn add(self, rhs: GF2) -> GF2 {
49        GF2(self.0 ^ rhs.0)
50    }
51}
52
53impl Sub for GF2 {
54    type Output = GF2;
55
56    #[allow(clippy::suspicious_arithmetic_impl)]
57    fn sub(self, rhs: GF2) -> GF2 {
58        self + rhs
59    }
60}
61
62impl Mul for GF2 {
63    type Output = GF2;
64
65    #[allow(clippy::suspicious_arithmetic_impl)]
66    fn mul(self, rhs: GF2) -> GF2 {
67        GF2(self.0 & rhs.0)
68    }
69}
70
71impl Div for GF2 {
72    type Output = GF2;
73
74    fn div(self, rhs: GF2) -> GF2 {
75        if rhs.is_zero() {
76            panic!("division by zero");
77        }
78        self
79    }
80}
81
82impl std::iter::Sum for GF2 {
83    fn sum<I>(iter: I) -> GF2
84    where
85        I: Iterator<Item = GF2>,
86    {
87        let mut sum = GF2::zero();
88        for a in iter {
89            sum += a;
90        }
91        sum
92    }
93}
94
95macro_rules! impl_ops {
96    ($op:ident, $opmethod:ident, $opassign:ident, $opassign_method:ident) => {
97        impl $op<&GF2> for GF2 {
98            type Output = GF2;
99            fn $opmethod(self, rhs: &GF2) -> GF2 {
100                self.$opmethod(*rhs)
101            }
102        }
103
104        impl $opassign for GF2 {
105            fn $opassign_method(&mut self, rhs: GF2) {
106                *self = self.$opmethod(rhs);
107            }
108        }
109
110        impl $opassign<&GF2> for GF2 {
111            fn $opassign_method(&mut self, rhs: &GF2) {
112                *self = self.$opmethod(*rhs);
113            }
114        }
115    };
116}
117
118impl_ops!(Add, add, AddAssign, add_assign);
119impl_ops!(Sub, sub, SubAssign, sub_assign);
120impl_ops!(Mul, mul, MulAssign, mul_assign);
121impl_ops!(Div, div, DivAssign, div_assign);
122
123impl ScalarOperand for GF2 {}
124
125#[cfg(test)]
126mod test {
127    use super::*;
128
129    #[test]
130    fn ops() {
131        assert_eq!(GF2(0) + GF2(0), GF2(0));
132        assert_eq!(GF2(0) + GF2(1), GF2(1));
133        assert_eq!(GF2(1) + GF2(0), GF2(1));
134        assert_eq!(GF2(1) + GF2(1), GF2(0));
135        assert_eq!(GF2(0) - GF2(0), GF2(0));
136        assert_eq!(GF2(0) - GF2(1), GF2(1));
137        assert_eq!(GF2(1) - GF2(0), GF2(1));
138        assert_eq!(GF2(1) - GF2(1), GF2(0));
139        assert_eq!(GF2(0) * GF2(0), GF2(0));
140        assert_eq!(GF2(0) * GF2(1), GF2(0));
141        assert_eq!(GF2(1) * GF2(0), GF2(0));
142        assert_eq!(GF2(1) * GF2(1), GF2(1));
143        assert_eq!(GF2(0) / GF2(1), GF2(0));
144        assert_eq!(GF2(1) / GF2(1), GF2(1));
145    }
146
147    #[test]
148    #[should_panic]
149    fn div_one_by_zero() {
150        let _a = GF2(1) / GF2(0);
151    }
152
153    #[test]
154    #[should_panic]
155    fn div_zero_by_zero() {
156        let _a = GF2(0) / GF2(0);
157    }
158
159    #[test]
160    fn sum() {
161        assert_eq!([GF2(0), GF2(1), GF2(1)].into_iter().sum::<GF2>(), GF2(0));
162        assert_eq!([GF2(0), GF2(1)].into_iter().sum::<GF2>(), GF2(1));
163        assert_eq!([GF2(1)].into_iter().sum::<GF2>(), GF2(1));
164        assert_eq!([].into_iter().sum::<GF2>(), GF2(0));
165    }
166}