use core::ops::{Add, Div, Mul, Sub};
use core::{
convert::From,
ops::{DivAssign, MulAssign, Neg, SubAssign},
};
use core::{iter::Sum, ops::AddAssign};
#[cfg(test)]
use alloc::vec::Vec;
#[cfg(test)]
use pretty_assertions::assert_eq;
const fn compute_alog_log() -> ([u8; 255], [u8; 256]) {
let mut alog = [0u8; 255];
let mut log = [0u8; 256];
let mut p: u16 = 1; let mut i: u8 = 0; while i < 255 {
alog[i as usize] = p as u8;
log[p as usize] = i;
p *= 2;
if p >= 256 {
p ^= 0x12D;
}
i += 1;
}
(alog, log)
}
const ANTI_LOG: [u8; 255] = compute_alog_log().0;
const LOG: [u8; 256] = compute_alog_log().1;
#[derive(Clone, Copy, PartialEq)]
pub struct GF(pub u8);
impl GF {
pub fn primitive_powers() -> impl Iterator<Item = Self> {
ANTI_LOG.iter().map(|x| Self(*x)).cycle()
}
pub fn primitive_power(i: u8) -> Self {
GF(ANTI_LOG[i as usize])
}
pub fn log(self) -> usize {
assert!(self != GF(0), "log of 0");
LOG[self.0 as usize] as usize
}
}
impl core::fmt::Debug for GF {
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
f.write_fmt(format_args!("{}₂₅₆", self.0))
}
}
impl Add<GF> for GF {
type Output = Self;
#[allow(clippy::suspicious_arithmetic_impl)]
fn add(self, rhs: Self) -> Self {
GF(self.0 ^ rhs.0)
}
}
impl AddAssign<GF> for GF {
fn add_assign(&mut self, rhs: GF) {
*self = *self + rhs;
}
}
impl Sub<GF> for GF {
type Output = Self;
#[allow(clippy::suspicious_arithmetic_impl)]
fn sub(self, rhs: Self) -> Self {
self + rhs
}
}
impl SubAssign<GF> for GF {
fn sub_assign(&mut self, rhs: GF) {
*self = *self - rhs;
}
}
impl Mul<GF> for GF {
type Output = Self;
fn mul(self, rhs: Self) -> Self {
if self.0 == 0 || rhs.0 == 0 {
return GF(0);
}
let ia = LOG[self.0 as usize];
let ib = LOG[rhs.0 as usize];
let i = (ia as u16 + ib as u16) % 255;
GF(ANTI_LOG[i as usize])
}
}
impl Mul<usize> for GF {
type Output = Self;
fn mul(self, rhs: usize) -> Self {
GF(self.0 * (rhs % 2) as u8)
}
}
impl MulAssign<GF> for GF {
fn mul_assign(&mut self, rhs: GF) {
*self = *self * rhs;
}
}
impl Div<GF> for GF {
type Output = Self;
fn div(self, rhs: Self) -> Self {
assert_ne!(rhs.0, 0, "division by zero");
if self.0 == 0 {
return GF(0);
}
let ia = LOG[self.0 as usize];
let ib = LOG[rhs.0 as usize];
let mut i = ia as i16 - ib as i16;
if i < 0 {
i += 255;
}
GF(ANTI_LOG[i as usize])
}
}
impl DivAssign<GF> for GF {
fn div_assign(&mut self, rhs: GF) {
*self = *self / rhs;
}
}
impl Neg for GF {
type Output = Self;
fn neg(self) -> Self {
Self(self.0)
}
}
impl From<GF> for u8 {
fn from(gf: GF) -> u8 {
gf.0
}
}
impl From<u8> for GF {
fn from(i: u8) -> Self {
GF(i)
}
}
impl Sum for GF {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(GF(0), |a, b| a + b)
}
}
#[test]
fn sanity_check_tables() {
use alloc::collections::BTreeSet;
let anti_log: BTreeSet<u8> = ANTI_LOG.iter().cloned().collect();
assert_eq!(anti_log.len(), ANTI_LOG.len());
let log: BTreeSet<u8> = LOG[1..].iter().cloned().collect();
assert_eq!(log.len(), LOG.len() - 1);
for i in 0..255 {
assert_eq!(i, LOG[ANTI_LOG[i] as usize] as usize);
assert_eq!(i + 1, ANTI_LOG[LOG[i + 1] as usize] as usize);
}
}
#[test]
fn gf256_mul() {
assert_eq!(GF(123) * GF(1), GF(123));
assert_eq!(GF(234) * GF(0), GF(0));
assert_eq!(GF(0) * GF(23), GF(0));
assert_eq!(GF(2) * GF(4) * GF(8) * GF(16) * GF(32), GF(228));
}
#[test]
fn gf256_div_mul() {
for a in 0..=255 {
for b in 1..=255 {
let a_div_b = GF(a) / GF(b);
assert_eq!(a_div_b * GF(b), GF(a));
}
}
}
#[test]
fn test_gf256_power_iterator() {
let powers: Vec<GF> = GF::primitive_powers().take(500).collect();
let mut power_direct = Vec::with_capacity(500);
let mut a = GF(1);
for i in 0..500 {
power_direct.push(a);
assert_eq!(GF::primitive_power((i % 255) as u8), a);
a *= GF(2);
}
assert_eq!(powers, power_direct);
}
#[test]
fn test_neg() {
for a in 0..255 {
let a = GF(a);
let ma = -a;
assert_eq!(a + ma, GF(0), "{:?}, {:?}", a, ma);
}
}
#[test]
#[allow(clippy::identity_op)]
fn test_mul_usize() {
assert_eq!(GF(5) * 1, GF(5));
assert_eq!(GF(5) * 2, GF(5) + GF(5));
}