1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
//! Finite field GF(2) arithmetic.
//!
//! This module contains the struct [GF2], which implements the finite field
//! arithmetic in GF(2).

use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
use ndarray::ScalarOperand;
use num_traits::{One, Zero};

/// Finite field GF(2) element.
///
/// This struct represents an element of the finite field GF(2).
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
pub struct GF2(u8);

impl Zero for GF2 {
    fn zero() -> GF2 {
        GF2(0)
    }

    fn is_zero(&self) -> bool {
        *self == Self::zero()
    }

    fn set_zero(&mut self) {
        *self = Self::zero()
    }
}

impl One for GF2 {
    fn one() -> GF2 {
        GF2(1)
    }

    fn set_one(&mut self) {
        *self = Self::one()
    }

    fn is_one(&self) -> bool {
        *self == Self::one()
    }
}

impl Add for GF2 {
    type Output = GF2;

    #[allow(clippy::suspicious_arithmetic_impl)]
    fn add(self, rhs: GF2) -> GF2 {
        GF2(self.0 ^ rhs.0)
    }
}

impl Sub for GF2 {
    type Output = GF2;

    #[allow(clippy::suspicious_arithmetic_impl)]
    fn sub(self, rhs: GF2) -> GF2 {
        self + rhs
    }
}

impl Mul for GF2 {
    type Output = GF2;

    #[allow(clippy::suspicious_arithmetic_impl)]
    fn mul(self, rhs: GF2) -> GF2 {
        GF2(self.0 & rhs.0)
    }
}

impl Div for GF2 {
    type Output = GF2;

    fn div(self, rhs: GF2) -> GF2 {
        if rhs.is_zero() {
            panic!("division by zero");
        }
        self
    }
}

macro_rules! impl_ops {
    ($op:ident, $opmethod:ident, $opassign:ident, $opassign_method:ident) => {
        impl $op<&GF2> for GF2 {
            type Output = GF2;
            fn $opmethod(self, rhs: &GF2) -> GF2 {
                self.$opmethod(*rhs)
            }
        }

        impl $opassign for GF2 {
            fn $opassign_method(&mut self, rhs: GF2) {
                *self = self.$opmethod(rhs);
            }
        }

        impl $opassign<&GF2> for GF2 {
            fn $opassign_method(&mut self, rhs: &GF2) {
                *self = self.$opmethod(*rhs);
            }
        }
    };
}

impl_ops!(Add, add, AddAssign, add_assign);
impl_ops!(Sub, sub, SubAssign, sub_assign);
impl_ops!(Mul, mul, MulAssign, mul_assign);
impl_ops!(Div, div, DivAssign, div_assign);

impl ScalarOperand for GF2 {}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn ops() {
        assert_eq!(GF2(0) + GF2(0), GF2(0));
        assert_eq!(GF2(0) + GF2(1), GF2(1));
        assert_eq!(GF2(1) + GF2(0), GF2(1));
        assert_eq!(GF2(1) + GF2(1), GF2(0));
        assert_eq!(GF2(0) - GF2(0), GF2(0));
        assert_eq!(GF2(0) - GF2(1), GF2(1));
        assert_eq!(GF2(1) - GF2(0), GF2(1));
        assert_eq!(GF2(1) - GF2(1), GF2(0));
        assert_eq!(GF2(0) * GF2(0), GF2(0));
        assert_eq!(GF2(0) * GF2(1), GF2(0));
        assert_eq!(GF2(1) * GF2(0), GF2(0));
        assert_eq!(GF2(1) * GF2(1), GF2(1));
        assert_eq!(GF2(0) / GF2(1), GF2(0));
        assert_eq!(GF2(1) / GF2(1), GF2(1));
    }

    #[test]
    #[should_panic]
    fn div_one_by_zero() {
        let _a = GF2(1) / GF2(0);
    }

    #[test]
    #[should_panic]
    fn div_zero_by_zero() {
        let _a = GF2(0) / GF2(0);
    }
}