use std::{
convert::{TryFrom, TryInto},
fmt, num, ops, str,
};
#[rustfmt::skip]
const LOG: [isize; 32] = [
0, 0, 1, 14, 2, 28, 15, 22,
3, 5, 29, 26, 16, 7, 23, 11,
4, 25, 6, 10, 30, 13, 27, 21,
17, 18, 8, 19, 24, 9, 12, 20,
];
#[rustfmt::skip]
const LOG_INV: [u8; 31] = [
1, 2, 4, 8, 16, 9, 18, 13,
26, 29, 19, 15, 30, 21, 3, 6,
12, 24, 25, 27, 31, 23, 7, 14,
28, 17, 11, 22, 5, 10, 20,
];
#[rustfmt::skip]
const CHARS_LOWER: [char; 32] = [
'q', 'p', 'z', 'r', 'y', '9', 'x', '8', 'g', 'f', '2', 't', 'v', 'd', 'w', '0', 's', '3', 'j', 'n', '5', '4', 'k', 'h', 'c', 'e', '6', 'm', 'u', 'a', '7', 'l', ];
#[rustfmt::skip]
const CHARS_INV: [i8; 128] = [
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1,
-1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
-1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
];
#[derive(Debug)]
pub enum Error {
ExtraChar(char),
NotAByte(num::TryFromIntError),
InvalidByte(u8),
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct Fe(u8);
impl ops::Add for Fe {
type Output = Fe;
fn add(self, other: Fe) -> Fe {
Fe(self.0 ^ other.0)
}
}
impl ops::AddAssign for Fe {
fn add_assign(&mut self, other: Fe) {
*self = *self + other;
}
}
impl ops::Sub for Fe {
type Output = Fe;
fn sub(self, other: Fe) -> Fe {
self + other
}
}
impl ops::SubAssign for Fe {
fn sub_assign(&mut self, other: Fe) {
*self = *self - other;
}
}
impl ops::Mul for Fe {
type Output = Fe;
fn mul(self, other: Fe) -> Fe {
if self.0 == 0 || other.0 == 0 {
Fe(0)
} else {
let log1 = LOG[self.0 as usize];
let log2 = LOG[other.0 as usize];
Fe(LOG_INV[((log1 + log2) % 31) as usize])
}
}
}
impl ops::MulAssign for Fe {
fn mul_assign(&mut self, other: Fe) {
*self = *self * other;
}
}
impl ops::Div for Fe {
type Output = Fe;
fn div(self, other: Fe) -> Fe {
if self.0 == 0 {
Fe(0)
} else if other.0 == 0 {
panic!("Attempt to divide {} by 0 in GF32", self);
} else {
let log1 = LOG[self.0 as usize];
let log2 = LOG[other.0 as usize];
Fe(LOG_INV[((31 + log1 - log2) % 31) as usize])
}
}
}
impl ops::DivAssign for Fe {
fn div_assign(&mut self, other: Fe) {
*self = *self / other;
}
}
impl Fe {
pub const Q: Fe = Fe(0);
pub const P: Fe = Fe(1);
#[allow(dead_code)]
pub const Z: Fe = Fe(2);
pub const R: Fe = Fe(3);
pub const Y: Fe = Fe(4);
pub const _9: Fe = Fe(5);
pub const X: Fe = Fe(6);
pub const _8: Fe = Fe(7);
pub const G: Fe = Fe(8);
pub const F: Fe = Fe(9);
pub const _2: Fe = Fe(10);
pub const T: Fe = Fe(11);
#[allow(dead_code)]
pub const V: Fe = Fe(12);
#[allow(dead_code)]
pub const D: Fe = Fe(13);
#[allow(dead_code)]
pub const W: Fe = Fe(14);
pub const _0: Fe = Fe(15);
pub const S: Fe = Fe(16);
pub const _3: Fe = Fe(17);
#[allow(dead_code)]
pub const J: Fe = Fe(18);
#[allow(dead_code)]
pub const N: Fe = Fe(19);
pub const _5: Fe = Fe(20);
pub const _4: Fe = Fe(21);
pub const K: Fe = Fe(22);
pub const H: Fe = Fe(23);
pub const C: Fe = Fe(24);
pub const E: Fe = Fe(25);
pub const _6: Fe = Fe(26);
pub const M: Fe = Fe(27);
#[allow(dead_code)]
pub const U: Fe = Fe(28);
pub const A: Fe = Fe(29);
pub const _7: Fe = Fe(30);
pub const L: Fe = Fe(31);
pub fn iter_alpha() -> impl Iterator<Item = Fe> {
[
Fe::A,
Fe::C,
Fe::D,
Fe::E,
Fe::F,
Fe::G,
Fe::H,
Fe::J,
Fe::K,
Fe::L,
Fe::M,
Fe::N,
Fe::P,
Fe::Q,
Fe::R,
Fe::S,
Fe::T,
Fe::U,
Fe::V,
Fe::W,
Fe::X,
Fe::Y,
Fe::Z,
Fe::_0,
Fe::_2,
Fe::_3,
Fe::_4,
Fe::_5,
Fe::_6,
Fe::_7,
Fe::_8,
Fe::_9,
]
.iter()
.copied()
}
pub fn from_u8(byte: u8) -> Result<Fe, super::Error> {
if byte < 32 {
Ok(Fe(byte))
} else {
Err(super::Error::Field(Error::InvalidByte(byte)))
}
}
pub fn from_int<I>(i: I) -> Result<Fe, super::Error>
where
I: TryInto<u8, Error = num::TryFromIntError>,
{
i.try_into()
.map_err(|e| super::Error::Field(Error::NotAByte(e)))
.and_then(Self::from_u8)
}
pub fn from_char(c: char) -> Result<Fe, super::Error> {
let byte = i8::try_from(u32::from(c)).map_err(|_| super::Error::InvalidChar(c))?;
let byte = byte as u8; let u5 =
u8::try_from(CHARS_INV[usize::from(byte)]).map_err(|_| super::Error::InvalidChar(c))?;
Ok(Fe(u5))
}
pub fn to_char(self) -> char {
CHARS_LOWER[self.0 as usize]
}
pub fn to_u8(self) -> u8 {
self.0
}
}
impl fmt::Display for Fe {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.to_char(), f)
}
}
impl str::FromStr for Fe {
type Err = super::Error;
fn from_str(s: &str) -> Result<Fe, super::Error> {
let mut chs = s.chars();
match (chs.next(), chs.next()) {
(Some(c), None) => Fe::from_char(c),
(Some(_), Some(c)) => Err(super::Error::Field(Error::ExtraChar(c))),
(None, _) => Err(super::Error::InvalidLength(0)),
}
}
}