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)),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn numeric_string() {
let s: String = (0..32).map(Fe).map(Fe::to_char).collect();
assert_eq!(s, "qpzry9x8gf2tvdw0s3jn54khce6mua7l");
}
#[test]
fn translation_wheel() {
let logbase = Fe(20);
let mut init = Fe(1);
let mut s = String::new();
for _ in 0..31 {
s.push(init.to_char());
init *= logbase;
}
assert_eq!(s, "p529kt3uw8hlmecvxr470na6djfsgyz");
let logbase = Fe(20);
let mut init = Fe(1);
let mut s = String::new();
for _ in 0..31 {
s.push(init.to_char());
init /= logbase;
}
assert_eq!(s, "pzygsfjd6an074rxvcemlh8wu3tk925");
}
#[test]
fn recovery_wheel() {
let logbase = Fe(10);
let mut init = Fe(1);
let mut s = String::new();
for _ in 0..31 {
s.push((init + Fe(16)).to_char());
init *= logbase;
}
assert_eq!(s, "36xp78tgk9ldaecjy4mvh0funwr2zq5");
}
}