use core::fmt;
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[repr(transparent)]
pub struct GF2(u8);
impl GF2 {
pub const ZERO: Self = GF2(0);
pub const ONE: Self = GF2(1);
#[inline]
#[must_use]
pub const fn new(value: u8) -> Self {
GF2(value & 1)
}
#[inline]
#[must_use]
pub const fn from_bool(value: bool) -> Self {
GF2(value as u8)
}
#[inline]
#[must_use]
pub const fn value(self) -> u8 {
self.0
}
#[inline]
#[must_use]
pub const fn is_zero_element(self) -> bool {
self.0 == 0
}
#[inline]
#[must_use]
pub const fn is_one(self) -> bool {
self.0 == 1
}
#[inline]
#[must_use]
pub const fn inverse(self) -> Option<Self> {
if self.0 == 1 {
Some(self)
} else {
None
}
}
}
#[allow(clippy::suspicious_arithmetic_impl)]
impl Add for GF2 {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self {
GF2(self.0 ^ rhs.0)
}
}
#[allow(clippy::suspicious_arithmetic_impl)]
impl Sub for GF2 {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self {
GF2(self.0 ^ rhs.0) }
}
#[allow(clippy::suspicious_arithmetic_impl)]
impl Mul for GF2 {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self {
GF2(self.0 & rhs.0)
}
}
impl Neg for GF2 {
type Output = Self;
#[inline]
fn neg(self) -> Self {
self }
}
#[allow(clippy::suspicious_op_assign_impl)]
impl AddAssign for GF2 {
#[inline]
fn add_assign(&mut self, rhs: Self) {
self.0 ^= rhs.0;
}
}
#[allow(clippy::suspicious_op_assign_impl)]
impl SubAssign for GF2 {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
self.0 ^= rhs.0;
}
}
#[allow(clippy::suspicious_op_assign_impl)]
impl MulAssign for GF2 {
#[inline]
fn mul_assign(&mut self, rhs: Self) {
self.0 &= rhs.0;
}
}
impl From<bool> for GF2 {
#[inline]
fn from(value: bool) -> Self {
GF2(value as u8)
}
}
impl From<u8> for GF2 {
#[inline]
fn from(value: u8) -> Self {
GF2(value & 1)
}
}
impl From<GF2> for bool {
#[inline]
fn from(value: GF2) -> Self {
value.0 != 0
}
}
impl From<GF2> for u8 {
#[inline]
fn from(value: GF2) -> Self {
value.0
}
}
impl fmt::Display for GF2 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl num_traits::Zero for GF2 {
#[inline]
fn zero() -> Self {
GF2::ZERO
}
#[inline]
fn is_zero(&self) -> bool {
self.0 == 0
}
}
impl num_traits::One for GF2 {
#[inline]
fn one() -> Self {
GF2::ONE
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_field_addition() {
assert_eq!(GF2::ZERO + GF2::ZERO, GF2::ZERO);
assert_eq!(GF2::ZERO + GF2::ONE, GF2::ONE);
assert_eq!(GF2::ONE + GF2::ZERO, GF2::ONE);
assert_eq!(GF2::ONE + GF2::ONE, GF2::ZERO);
}
#[test]
fn test_field_multiplication() {
assert_eq!(GF2::ZERO * GF2::ZERO, GF2::ZERO);
assert_eq!(GF2::ZERO * GF2::ONE, GF2::ZERO);
assert_eq!(GF2::ONE * GF2::ZERO, GF2::ZERO);
assert_eq!(GF2::ONE * GF2::ONE, GF2::ONE);
}
#[test]
fn test_distributivity() {
for &a in &[GF2::ZERO, GF2::ONE] {
for &b in &[GF2::ZERO, GF2::ONE] {
for &c in &[GF2::ZERO, GF2::ONE] {
assert_eq!(a * (b + c), a * b + a * c);
}
}
}
}
#[test]
fn test_self_inverse() {
assert_eq!(GF2::ZERO + GF2::ZERO, GF2::ZERO);
assert_eq!(GF2::ONE + GF2::ONE, GF2::ZERO);
assert_eq!(-GF2::ZERO, GF2::ZERO);
assert_eq!(-GF2::ONE, GF2::ONE);
}
#[test]
fn test_multiplicative_inverse() {
assert_eq!(GF2::ONE.inverse(), Some(GF2::ONE));
assert_eq!(GF2::ZERO.inverse(), None);
}
#[test]
fn test_conversions() {
assert_eq!(GF2::from(true), GF2::ONE);
assert_eq!(GF2::from(false), GF2::ZERO);
assert_eq!(GF2::new(5), GF2::ONE); assert_eq!(GF2::new(4), GF2::ZERO); assert!(bool::from(GF2::ONE));
assert!(!bool::from(GF2::ZERO));
}
}