1use core::convert::{Infallible, TryFrom};
16use core::{fmt, num, ops};
17
18#[cfg(all(test, mutate))]
19use mutagen::mutate;
20
21use crate::error::write_err;
22
23#[rustfmt::skip]
28const LOG: [isize; 32] = [
29 0, 0, 1, 14, 2, 28, 15, 22,
30 3, 5, 29, 26, 16, 7, 23, 11,
31 4, 25, 6, 10, 30, 13, 27, 21,
32 17, 18, 8, 19, 24, 9, 12, 20,
33];
34
35#[rustfmt::skip]
37const LOG_INV: [u8; 31] = [
38 1, 2, 4, 8, 16, 9, 18, 13,
39 26, 29, 19, 15, 30, 21, 3, 6,
40 12, 24, 25, 27, 31, 23, 7, 14,
41 28, 17, 11, 22, 5, 10, 20,
42];
43
44#[rustfmt::skip]
46const CHARS_LOWER: [char; 32] = [
47 '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', ];
52
53#[rustfmt::skip]
57const CHARS_INV: [i8; 128] = [
58 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
59 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
60 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
61 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1,
62 -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
63 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
64 -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
65 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
66];
67
68#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
70#[repr(transparent)]
71pub struct Fe32(pub(crate) u8);
72
73impl Fe32 {
74 pub const Q: Fe32 = Fe32(0);
77 pub const P: Fe32 = Fe32(1);
79 pub const Z: Fe32 = Fe32(2);
81 pub const R: Fe32 = Fe32(3);
83 pub const Y: Fe32 = Fe32(4);
85 pub const _9: Fe32 = Fe32(5);
87 pub const X: Fe32 = Fe32(6);
89 pub const _8: Fe32 = Fe32(7);
91 pub const G: Fe32 = Fe32(8);
93 pub const F: Fe32 = Fe32(9);
95 pub const _2: Fe32 = Fe32(10);
97 pub const T: Fe32 = Fe32(11);
99 pub const V: Fe32 = Fe32(12);
101 pub const D: Fe32 = Fe32(13);
103 pub const W: Fe32 = Fe32(14);
105 pub const _0: Fe32 = Fe32(15);
107 pub const S: Fe32 = Fe32(16);
109 pub const _3: Fe32 = Fe32(17);
111 pub const J: Fe32 = Fe32(18);
113 pub const N: Fe32 = Fe32(19);
115 pub const _5: Fe32 = Fe32(20);
117 pub const _4: Fe32 = Fe32(21);
119 pub const K: Fe32 = Fe32(22);
121 pub const H: Fe32 = Fe32(23);
123 pub const C: Fe32 = Fe32(24);
125 pub const E: Fe32 = Fe32(25);
127 pub const _6: Fe32 = Fe32(26);
129 pub const M: Fe32 = Fe32(27);
131 pub const U: Fe32 = Fe32(28);
133 pub const A: Fe32 = Fe32(29);
135 pub const _7: Fe32 = Fe32(30);
137 pub const L: Fe32 = Fe32(31);
139
140 #[inline]
142 pub fn iter_alpha() -> impl Iterator<Item = Fe32> {
143 [
144 Fe32::A,
145 Fe32::C,
146 Fe32::D,
147 Fe32::E,
148 Fe32::F,
149 Fe32::G,
150 Fe32::H,
151 Fe32::J,
152 Fe32::K,
153 Fe32::L,
154 Fe32::M,
155 Fe32::N,
156 Fe32::P,
157 Fe32::Q,
158 Fe32::R,
159 Fe32::S,
160 Fe32::T,
161 Fe32::U,
162 Fe32::V,
163 Fe32::W,
164 Fe32::X,
165 Fe32::Y,
166 Fe32::Z,
167 Fe32::_0,
168 Fe32::_2,
169 Fe32::_3,
170 Fe32::_4,
171 Fe32::_5,
172 Fe32::_6,
173 Fe32::_7,
174 Fe32::_8,
175 Fe32::_9,
176 ]
177 .iter()
178 .copied()
179 }
180
181 #[inline]
187 pub fn from_char(c: char) -> Result<Fe32, FromCharError> {
188 use FromCharError::*;
189
190 let byte = i8::try_from(u32::from(c)).map_err(|_| NotAscii(c))?;
192 let ascii = byte as usize;
194 let u5 = u8::try_from(CHARS_INV[ascii]).map_err(|_| Invalid(c))?;
196
197 Ok(Fe32(u5))
198 }
199
200 pub fn from_char_unchecked(c: u8) -> Fe32 { Fe32(CHARS_INV[usize::from(c)] as u8) }
206
207 #[inline]
209 pub fn to_char(self) -> char {
210 CHARS_LOWER[usize::from(self.0)]
212 }
213
214 #[inline]
217 pub fn to_u8(self) -> u8 { self.0 }
218
219 fn _add(self, other: Fe32) -> Fe32 { Fe32(self.0 ^ other.0) }
220
221 fn _sub(self, other: Fe32) -> Fe32 { self + other }
223
224 #[cfg_attr(all(test, mutate), mutate)]
225 fn _mul(self, other: Fe32) -> Fe32 {
226 if self.0 == 0 || other.0 == 0 {
227 Fe32(0)
228 } else {
229 let log1 = LOG[self.0 as usize];
230 let log2 = LOG[other.0 as usize];
231 Fe32(LOG_INV[((log1 + log2) % 31) as usize])
232 }
233 }
234
235 #[cfg_attr(all(test, mutate), mutate)]
236 fn _div(self, other: Fe32) -> Fe32 {
237 if self.0 == 0 {
238 Fe32(0)
239 } else if other.0 == 0 {
240 panic!("Attempt to divide {} by 0 in GF32", self);
241 } else {
242 let log1 = LOG[self.0 as usize];
243 let log2 = LOG[other.0 as usize];
244 Fe32(LOG_INV[((31 + log1 - log2) % 31) as usize])
245 }
246 }
247}
248
249impl fmt::Display for Fe32 {
250 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.to_char(), f) }
251}
252
253impl From<Fe32> for u8 {
254 #[inline]
255 fn from(v: Fe32) -> u8 { v.0 }
256}
257
258macro_rules! impl_try_from {
259 ($($ty:ident)+) => {
260 $(
261 impl TryFrom<$ty> for Fe32 {
262 type Error = TryFromError;
263
264 #[inline]
270 fn try_from(value: $ty) -> Result<Self, Self::Error> {
271 let byte = u8::try_from(value)?;
272 if byte > 31 {
273 Err(TryFromError::InvalidByte(byte))?;
274 }
275 Ok(Fe32(byte))
276 }
277 }
278 )+
279 }
280}
281impl_try_from!(u8 u16 u32 u64 u128 i8 i16 i32 i64 i128);
282
283impl AsRef<u8> for Fe32 {
284 #[inline]
285 fn as_ref(&self) -> &u8 { &self.0 }
286}
287
288macro_rules! impl_op_matrix {
290 ($op:ident, $op_fn:ident, $call_fn:ident) => {
291 impl ops::$op<Fe32> for Fe32 {
292 type Output = Fe32;
293 #[inline]
294 fn $op_fn(self, other: Fe32) -> Fe32 { self.$call_fn(other) }
295 }
296
297 impl ops::$op<Fe32> for &Fe32 {
298 type Output = Fe32;
299 #[inline]
300 fn $op_fn(self, other: Fe32) -> Fe32 { self.$call_fn(other) }
301 }
302
303 impl ops::$op<&Fe32> for Fe32 {
304 type Output = Fe32;
305 #[inline]
306 fn $op_fn(self, other: &Fe32) -> Fe32 { self.$call_fn(*other) }
307 }
308
309 impl ops::$op<&Fe32> for &Fe32 {
310 type Output = Fe32;
311 #[inline]
312 fn $op_fn(self, other: &Fe32) -> Fe32 { self.$call_fn(*other) }
313 }
314 };
315}
316impl_op_matrix!(Add, add, _add);
317impl_op_matrix!(Sub, sub, _sub);
318impl_op_matrix!(Mul, mul, _mul);
319impl_op_matrix!(Div, div, _div);
320
321impl ops::AddAssign for Fe32 {
322 #[inline]
323 fn add_assign(&mut self, other: Fe32) { *self = *self + other; }
324}
325
326impl ops::SubAssign for Fe32 {
327 #[inline]
328 fn sub_assign(&mut self, other: Fe32) { *self = *self - other; }
329}
330
331impl ops::MulAssign for Fe32 {
332 #[inline]
333 fn mul_assign(&mut self, other: Fe32) { *self = *self * other; }
334}
335
336impl ops::DivAssign for Fe32 {
337 #[inline]
338 fn div_assign(&mut self, other: Fe32) { *self = *self / other; }
339}
340
341#[derive(Copy, Clone, PartialEq, Eq, Debug)]
343#[non_exhaustive]
344pub enum FromCharError {
345 NotAscii(char),
347 Invalid(char),
349}
350
351impl fmt::Display for FromCharError {
352 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
353 use FromCharError::*;
354
355 match *self {
356 NotAscii(c) => write!(f, "non-ascii char in field element: {}", c),
357 Invalid(c) => write!(f, "invalid char in field element: {}", c),
358 }
359 }
360}
361
362#[cfg(feature = "std")]
363impl std::error::Error for FromCharError {
364 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
365 use FromCharError::*;
366
367 match *self {
368 NotAscii(_) | Invalid(_) => None,
369 }
370 }
371}
372
373#[derive(Copy, Clone, PartialEq, Eq, Debug)]
375#[non_exhaustive]
376pub enum TryFromError {
377 NotAByte(num::TryFromIntError),
379 InvalidByte(u8),
381}
382
383impl fmt::Display for TryFromError {
384 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
385 use TryFromError::*;
386
387 match *self {
388 NotAByte(ref e) => write_err!(f, "invalid field element"; e),
389 InvalidByte(ref b) => write!(f, "invalid byte in field element: {:#04x}", b),
390 }
391 }
392}
393
394#[cfg(feature = "std")]
395impl std::error::Error for TryFromError {
396 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
397 use TryFromError::*;
398
399 match *self {
400 NotAByte(ref e) => Some(e),
401 InvalidByte(_) => None,
402 }
403 }
404}
405
406impl From<num::TryFromIntError> for TryFromError {
407 #[inline]
408 fn from(e: num::TryFromIntError) -> Self { Self::NotAByte(e) }
409}
410
411impl From<Infallible> for TryFromError {
412 #[inline]
413 fn from(i: Infallible) -> Self { match i {} }
414}
415
416#[cfg(test)]
417mod tests {
418 use super::*;
419
420 #[test]
421 fn numeric_string() {
422 let s: String = (0..32).map(Fe32).map(Fe32::to_char).collect();
423 assert_eq!(s, "qpzry9x8gf2tvdw0s3jn54khce6mua7l");
424 }
425
426 #[test]
429 fn translation_wheel() {
430 let logbase = Fe32(20);
432 let mut init = Fe32(1);
433 let mut s = String::new();
434 for _ in 0..31 {
435 s.push(init.to_char());
436 init *= logbase;
437 }
438 assert_eq!(s, "p529kt3uw8hlmecvxr470na6djfsgyz");
440
441 let logbase = Fe32(20);
443 let mut init = Fe32(1);
444 let mut s = String::new();
445 for _ in 0..31 {
446 s.push(init.to_char());
447 init /= logbase;
448 }
449 assert_eq!(s, "pzygsfjd6an074rxvcemlh8wu3tk925");
451 }
452
453 #[test]
456 fn recovery_wheel() {
457 let logbase = Fe32(10);
464 let mut init = Fe32(1);
465 let mut s = String::new();
466 for _ in 0..31 {
467 s.push((init + Fe32(16)).to_char());
468 init *= logbase;
469 }
470 assert_eq!(s, "36xp78tgk9ldaecjy4mvh0funwr2zq5");
472 }
473
474 #[test]
475 fn reverse_charset() {
476 fn get_char_value(c: char) -> i8 {
477 let charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
478 match charset.find(c.to_ascii_lowercase()) {
479 Some(x) => x as i8,
480 None => -1,
481 }
482 }
483
484 let expected_rev_charset =
485 (0u8..128).map(|i| get_char_value(i as char)).collect::<Vec<_>>();
486
487 assert_eq!(&(CHARS_INV[..]), expected_rev_charset.as_slice());
488 }
489
490 #[test]
491 fn from_char() {
492 for c in &CHARS_LOWER[..] {
493 assert!(Fe32::from_char(*c).is_ok())
494 }
495 }
496
497 #[test]
498 fn from_upper_char() {
499 let lower = Fe32::from_char('q').expect("failed to create fe32 from lowercase ascii char");
500 let upper = Fe32::from_char('Q').expect("failed to create fe32 from uppercase ascii char");
501
502 assert_eq!(lower, upper);
503 }
504
505 #[test]
506 fn mul_zero() {
507 for c in &CHARS_LOWER[..] {
508 let fe = Fe32::from_char(*c).unwrap();
509 assert_eq!(fe._mul(Fe32::Q), Fe32::Q) }
511 }
512
513 #[test]
514 #[should_panic]
515 fn div_zero() {
516 let _ = Fe32::P / Fe32::Q; }
518
519 #[test]
520 fn div_self_zero() {
521 let fe = Fe32::Z; assert_eq!(Fe32::Q / fe, Fe32::Q) }
524
525 #[test]
526 fn mul_one() {
527 for c in &CHARS_LOWER[..] {
528 let fe = Fe32::from_char(*c).unwrap();
529 assert_eq!(fe * Fe32::P, fe) }
531 }
532}
533
534#[cfg(kani)]
535mod verification {
536 use super::*;
537
538 #[kani::proof]
539 fn check_char_conversion() {
540 let any: char = kani::any();
541 if let Ok(fe) = Fe32::from_char(any) {
543 let got = fe.to_char();
544 let want = any.to_ascii_lowercase();
545 assert_eq!(got, want);
546 }
547 }
548}