use core::convert::{Infallible, TryFrom};
use core::{fmt, num};
use super::{Bech32Field, Field};
use crate::error::write_err;
#[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(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct Fe32(pub(crate) u8);
impl Fe32 {
pub const Q: Fe32 = Fe32(0);
pub const P: Fe32 = Fe32(1);
pub const Z: Fe32 = Fe32(2);
pub const R: Fe32 = Fe32(3);
pub const Y: Fe32 = Fe32(4);
pub const _9: Fe32 = Fe32(5);
pub const X: Fe32 = Fe32(6);
pub const _8: Fe32 = Fe32(7);
pub const G: Fe32 = Fe32(8);
pub const F: Fe32 = Fe32(9);
pub const _2: Fe32 = Fe32(10);
pub const T: Fe32 = Fe32(11);
pub const V: Fe32 = Fe32(12);
pub const D: Fe32 = Fe32(13);
pub const W: Fe32 = Fe32(14);
pub const _0: Fe32 = Fe32(15);
pub const S: Fe32 = Fe32(16);
pub const _3: Fe32 = Fe32(17);
pub const J: Fe32 = Fe32(18);
pub const N: Fe32 = Fe32(19);
pub const _5: Fe32 = Fe32(20);
pub const _4: Fe32 = Fe32(21);
pub const K: Fe32 = Fe32(22);
pub const H: Fe32 = Fe32(23);
pub const C: Fe32 = Fe32(24);
pub const E: Fe32 = Fe32(25);
pub const _6: Fe32 = Fe32(26);
pub const M: Fe32 = Fe32(27);
pub const U: Fe32 = Fe32(28);
pub const A: Fe32 = Fe32(29);
pub const _7: Fe32 = Fe32(30);
pub const L: Fe32 = Fe32(31);
#[inline]
pub fn iter_alpha() -> impl Iterator<Item = Fe32> {
[
Fe32::A,
Fe32::C,
Fe32::D,
Fe32::E,
Fe32::F,
Fe32::G,
Fe32::H,
Fe32::J,
Fe32::K,
Fe32::L,
Fe32::M,
Fe32::N,
Fe32::P,
Fe32::Q,
Fe32::R,
Fe32::S,
Fe32::T,
Fe32::U,
Fe32::V,
Fe32::W,
Fe32::X,
Fe32::Y,
Fe32::Z,
Fe32::_0,
Fe32::_2,
Fe32::_3,
Fe32::_4,
Fe32::_5,
Fe32::_6,
Fe32::_7,
Fe32::_8,
Fe32::_9,
]
.iter()
.copied()
}
#[inline]
pub fn from_char(c: char) -> Result<Fe32, FromCharError> {
use FromCharError::*;
let byte = i8::try_from(u32::from(c)).map_err(|_| NotAscii(c))?;
let ascii = byte as usize;
let u5 = u8::try_from(CHARS_INV[ascii]).map_err(|_| Invalid(c))?;
Ok(Fe32(u5))
}
pub fn from_char_unchecked(c: u8) -> Fe32 { Fe32(CHARS_INV[usize::from(c)] as u8) }
#[inline]
pub fn to_char(self) -> char {
CHARS_LOWER[usize::from(self.0)]
}
#[inline]
pub fn to_u8(self) -> u8 { self.0 }
}
impl fmt::Display for Fe32 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.to_char(), f) }
}
impl From<Fe32> for u8 {
#[inline]
fn from(v: Fe32) -> u8 { v.0 }
}
macro_rules! impl_try_from {
($($ty:ident)+) => {
$(
impl TryFrom<$ty> for Fe32 {
type Error = TryFromError;
#[inline]
fn try_from(value: $ty) -> Result<Self, Self::Error> {
let byte = u8::try_from(value)?;
if byte > 31 {
Err(TryFromError::InvalidByte(byte))?;
}
Ok(Fe32(byte))
}
}
)+
}
}
impl_try_from!(u8 u16 u32 u64 u128 i8 i16 i32 i64 i128);
impl AsRef<u8> for Fe32 {
#[inline]
fn as_ref(&self) -> &u8 { &self.0 }
}
impl Bech32Field for Fe32 {
fn _add(&self, other: &Fe32) -> Fe32 { Fe32(self.0 ^ other.0) }
fn _mul(&self, other: &Fe32) -> Fe32 {
if self.0 == 0 || other.0 == 0 {
Fe32(0)
} else {
let log1 = LOG[usize::from(self.0)];
let log2 = LOG[usize::from(other.0)];
let mult_order = Self::MULTIPLICATIVE_ORDER as isize;
Fe32(LOG_INV[((log1 + log2) % mult_order) as usize])
}
}
fn _div(&self, other: &Fe32) -> Fe32 {
if self.0 == 0 {
Fe32(0)
} else if other.0 == 0 {
panic!("Attempt to divide {} by 0 in GF32", self);
} else {
let log1 = LOG[usize::from(self.0)];
let log2 = LOG[usize::from(other.0)];
let mult_order = Self::MULTIPLICATIVE_ORDER as isize;
Fe32(LOG_INV[((mult_order + log1 - log2) % mult_order) as usize])
}
}
fn _neg(self) -> Self { self }
fn format_as_rust_code(&self, f: &mut fmt::Formatter) -> fmt::Result {
let ch = self.to_char().to_ascii_uppercase();
if ch.is_ascii_digit() {
write!(f, "Fe32::_{}", ch)
} else {
write!(f, "Fe32::{}", ch)
}
}
}
impl Field for Fe32 {
const ZERO: Self = Fe32::Q;
const ONE: Self = Fe32::P;
const GENERATOR: Self = Fe32::Z;
const CHARACTERISTIC: usize = 2;
const MULTIPLICATIVE_ORDER: usize = 31;
const MULTIPLICATIVE_ORDER_FACTORS: &'static [usize] = &[1, 31];
fn multiplicative_inverse(self) -> Self { Self::ONE._div(&self) }
}
super::impl_ops_for_fe!(impl for Fe32);
impl super::ExtensionField for Fe32 {
type BaseField = Self;
const DEGREE: usize = 1;
const POLYNOMIAL: Self = Fe32::Q;
const EXT_ELEM: Self = Fe32::P;
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[non_exhaustive]
pub enum FromCharError {
NotAscii(char),
Invalid(char),
}
impl fmt::Display for FromCharError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use FromCharError::*;
match *self {
NotAscii(c) => write!(f, "non-ascii char in field element: {}", c),
Invalid(c) => write!(f, "invalid char in field element: {}", c),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for FromCharError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
use FromCharError::*;
match *self {
NotAscii(_) | Invalid(_) => None,
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[non_exhaustive]
pub enum TryFromError {
NotAByte(num::TryFromIntError),
InvalidByte(u8),
}
impl fmt::Display for TryFromError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use TryFromError::*;
match *self {
NotAByte(ref e) => write_err!(f, "invalid field element"; e),
InvalidByte(ref b) => write!(f, "invalid byte in field element: {:#04x}", b),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for TryFromError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
use TryFromError::*;
match *self {
NotAByte(ref e) => Some(e),
InvalidByte(_) => None,
}
}
}
impl From<num::TryFromIntError> for TryFromError {
#[inline]
fn from(e: num::TryFromIntError) -> Self { Self::NotAByte(e) }
}
impl From<Infallible> for TryFromError {
#[inline]
fn from(i: Infallible) -> Self { match i {} }
}
#[cfg(test)]
mod tests {
#[cfg(feature = "std")]
use core::convert::TryFrom;
use super::*;
#[test]
fn numeric_string() {
let s: String = (0..32).map(Fe32).map(Fe32::to_char).collect();
assert_eq!(s, "qpzry9x8gf2tvdw0s3jn54khce6mua7l");
}
#[test]
fn translation_wheel() {
let logbase = Fe32(20);
let mut init = Fe32(1);
let mut s = String::new();
for _ in 0..31 {
s.push(init.to_char());
init *= logbase;
}
assert_eq!(s, "p529kt3uw8hlmecvxr470na6djfsgyz");
let logbase = Fe32(20);
let mut init = Fe32(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 = Fe32(10);
let mut init = Fe32(1);
let mut s = String::new();
for _ in 0..31 {
s.push((init + Fe32(16)).to_char());
init *= logbase;
}
assert_eq!(s, "36xp78tgk9ldaecjy4mvh0funwr2zq5");
}
#[test]
fn reverse_charset() {
fn get_char_value(c: char) -> i8 {
let charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
match charset.find(c.to_ascii_lowercase()) {
Some(x) => x as i8,
None => -1,
}
}
let expected_rev_charset =
(0u8..128).map(|i| get_char_value(i as char)).collect::<Vec<_>>();
assert_eq!(&(CHARS_INV[..]), expected_rev_charset.as_slice());
}
#[test]
fn from_char() {
for c in &CHARS_LOWER[..] {
assert!(Fe32::from_char(*c).is_ok())
}
}
#[test]
fn from_upper_char() {
let lower = Fe32::from_char('q').expect("failed to create fe32 from lowercase ascii char");
let upper = Fe32::from_char('Q').expect("failed to create fe32 from uppercase ascii char");
assert_eq!(lower, upper);
}
#[test]
fn mul_zero() {
for c in &CHARS_LOWER[..] {
let fe = Fe32::from_char(*c).unwrap();
assert_eq!(fe * Fe32::Q, Fe32::Q) }
}
#[test]
#[should_panic]
fn div_zero() {
let _ = Fe32::P / Fe32::Q; }
#[test]
fn div_self_zero() {
let fe = Fe32::Z; assert_eq!(Fe32::Q / fe, Fe32::Q) }
#[test]
fn mul_one() {
for c in &CHARS_LOWER[..] {
let fe = Fe32::from_char(*c).unwrap();
assert_eq!(fe * Fe32::P, fe) }
}
#[test]
fn default() {
assert_eq!(Fe32::default().to_u8(), 0);
assert_eq!(Fe32::default(), Fe32::Q);
}
#[test]
fn iter_alpha() {
let elems: Vec<_> = Fe32::iter_alpha().collect();
assert_eq!(elems.len(), 32);
let mut seen = [false; 32];
for fe in &elems {
seen[fe.0 as usize] = true;
}
}
#[test]
fn field_arithmetic() {
assert_eq!(*Fe32::L.as_ref(), 31u8);
for i in 0..32 {
let fe = Fe32(i);
assert_eq!(-fe, fe);
}
for i in 1..32 {
let fe = Fe32(i);
let inv = fe.multiplicative_inverse();
assert_ne!(inv, Fe32::default(), "inverse of {} should not be default", i);
}
}
#[test]
#[cfg(feature = "std")]
fn conversion_error_display_and_source() {
let invalid = Fe32::from_char('b').unwrap_err();
assert!(!invalid.to_string().is_empty());
assert!(std::error::Error::source(&invalid).is_none());
let not_a_byte = Fe32::try_from(256u32).unwrap_err();
assert!(!not_a_byte.to_string().is_empty());
assert!(std::error::Error::source(¬_a_byte).is_some());
}
#[test]
fn format_as_rust_code() {
struct RustCode(Fe32);
impl core::fmt::Display for RustCode {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
Bech32Field::format_as_rust_code(&self.0, f)
}
}
assert_eq!(RustCode(Fe32::A).to_string(), "Fe32::A");
}
}
#[cfg(kani)]
mod verification {
use super::*;
#[kani::proof]
fn check_char_conversion() {
let any: char = kani::any();
if let Ok(fe) = Fe32::from_char(any) {
let got = fe.to_char();
let want = any.to_ascii_lowercase();
assert_eq!(got, want);
}
}
}