use std::ops::{Add, Div, Mul, Sub};
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, BinaryArithmetic)]
pub struct Poly2(u64);
impl Poly2 {
pub fn new(n: u64) -> Self {
Self(n)
}
pub fn as_u64(&self) -> u64 {
self.0
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, BinaryArithmetic)]
pub struct GF2(u8);
impl GF2 {
pub fn new(n: u8) -> Self {
Self(n)
}
pub fn as_u8(&self) -> u8 {
self.0
}
}
impl Div for GF2 {
type Output = Self;
fn div(self, _rhs: Self) -> Self::Output {
self
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
pub struct GenGF2E {
extension: u16,
polynomial: Poly2,
}
impl GenGF2E {
pub fn new(extension: u16, polynomial: Poly2) -> Self {
Self {
extension,
polynomial,
}
}
pub fn gen(&self, n: Poly2) -> GF2E {
GF2E::new(n, self.extension, self.polynomial)
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
pub struct GF2E(u64, u16, Poly2);
impl GF2E {
fn new(n: Poly2, ext: u16, p: Poly2) -> Self {
Self(n.as_u64(), ext, p)
}
pub fn as_u64(&self) -> u64 {
self.0
}
pub fn inverse(&self) -> Option<Self> {
let p = self.polynomial();
let ext = self.extension();
gf2_inverse(self.0, (self.2).0, self.1).map(|n| Self(n, ext, p))
}
fn extension(&self) -> u16 {
self.1
}
fn polynomial(&self) -> Poly2 {
self.2
}
}
impl Add for GF2E {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
let n = gf2_add(self.0, rhs.0);
GF2E(n, self.1, self.2)
}
}
impl Sub for GF2E {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
let n = gf2_sub(self.0, rhs.0);
GF2E(n, self.1, self.2)
}
}
impl Mul for GF2E {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
let p = (self.2).0;
let n = gf2_multiply(self.0, rhs.0, p, self.1);
GF2E(n, self.1, self.2)
}
}
#[inline]
fn gf2_add(a: u64, b: u64) -> u64 {
a ^ b
}
#[inline]
fn gf2_sub(a: u64, b: u64) -> u64 {
a ^ b
}
fn gf2_multiply(mut a: u64, mut b: u64, p: u64, ext: u16) -> u64 {
let mut c = 0;
let overflow = 2u64.pow((ext - 1) as u32);
while a != 0 && b != 0 {
if (b & 1) != 0 {
c ^= a;
}
if a >= overflow {
a = (a << 1) ^ p;
} else {
a <<= 1;
}
b >>= 1;
}
c
}
#[allow(dead_code)]
fn gf2_inverse_primitive(a: u64, ext: u16) -> Option<u64> {
if a == 0 {
return None;
}
let mut b = 1;
let i = 2u64.pow(ext as u32) - 2;
for _ in 0..i {
b *= a;
}
Some(b)
}
fn _inverse(a: u64, p: u64) -> Option<u64> {
let (g, x, _) = gcde2(p, a);
if g == 1 {
let (_, r) = euclidean_division2(x, p);
Some(r)
} else {
None
}
}
fn gf2_inverse(a: u64, p: u64, ext: u16) -> Option<u64> {
let mut x = p;
let mut x1 = 1;
let mut y = 1;
let mut y1 = 0;
let mut n = a;
let mut m = p;
while m != 0 {
let (q, r) = euclidean_division2(n, m);
n = m;
m = r;
let x2 = x;
x = x1 ^ gf2_multiply(q, x, p, ext);
x1 = x2;
let y2 = y;
y = y1 ^ gf2_multiply(q, y, p, ext);
y1 = y2;
}
if n == 1 {
let (_, r) = euclidean_division2(x1 ^ p, p);
Some(r)
} else {
None
}
}
fn clz_u64(mut x: u64) -> u8 {
if x == 0 {
64
} else {
let mut n = 0;
while (x & 0x8000000000000000) == 0 {
n += 1;
x <<= 1;
}
n
}
}
fn degree(n: u64) -> u8 {
match clz_u64(n) {
64 => 0,
n => 63 - n,
}
}
fn lc2(n: u64) -> u8 {
if n == 0 || n == 1 {
0
} else {
1
}
}
fn euclidean_division2(a: u64, b: u64) -> (u64, u64) {
if b == 1 {
return (a, 0);
}
let mut q = 0;
let mut r = a;
let db = degree(b);
let lcb = lc2(b);
while degree(r) >= db {
let lcr = lc2(r);
let t = if lcr == 0 || lcb == 0 {
0
} else {
(degree(r) - db) as u64
};
r ^= b << t;
q ^= 1 << t;
}
(q, r)
}
#[allow(dead_code)]
fn gcde2(mut a: u64, mut b: u64) -> (u64, u64, u64) {
let mut x0 = 1;
let mut y0 = 0;
let mut x1 = 0;
let mut y1 = 1;
while b != 0 {
let (q, r) = euclidean_division2(a, b);
a = b;
b = r;
let x2 = x0;
x0 = x1;
x1 = x2 ^ q & x1;
let y2 = y0;
y0 = y1;
y1 = y2 ^ q & y1;
}
(a, x0, y0)
}
#[cfg(test)]
mod tests {
use super::*;
use std::u64;
#[test]
fn clz_u64_works() {
assert_eq!(64, clz_u64(0));
assert_eq!(63, clz_u64(0b01));
assert_eq!(0, clz_u64(u64::MAX));
}
#[test]
fn degree_works() {
assert_eq!(0, degree(0));
assert_eq!(0, degree(0b01));
assert_eq!(1, degree(0b011));
assert_eq!(2, degree(0b101));
assert_eq!(63, degree(u64::MAX));
}
#[test]
fn lc2_works() {
assert_eq!(0, lc2(0));
assert_eq!(0, lc2(0b01));
assert_eq!(1, lc2(0b1000));
assert_eq!(1, lc2(u64::MAX));
}
#[test]
fn euclidean_division2_works() {
assert_eq!((0b11, 0b1), euclidean_division2(0b1011, 0b110));
assert_eq!((0b110, 0), euclidean_division2(0b110, 0b1));
assert_eq!((0b111, 0b1), euclidean_division2(0b11010, 0b101));
assert_eq!((0b101, 0b100), euclidean_division2(0b100011011, 0b1010011));
}
#[test]
fn gcde2_works() {
assert_eq!((0b1, 0b1, 0b1), gcde2(0b1011, 0b110));
assert_eq!((0b110, 0, 0b1), gcde2(0b1010, 0b110));
}
#[test]
fn poly2_add_works() {
let p1 = Poly2(0b100011011);
let p2 = Poly2(0b110001001);
assert_eq!(Poly2(0b010010010), p1 + p2);
}
#[test]
fn poly2_multiply_works() {
let p1 = Poly2(0b100011011);
let p2 = Poly2(0b110001001);
assert_eq!(Poly2(0b100001001), p1 * p2);
}
#[test]
fn gf2e_add_works() {
let p = Poly2::new(0b10011);
let g = GenGF2E::new(4, p);
let a = g.gen(Poly2(0b1010));
let b = g.gen(Poly2(0b0110));
assert_eq!(g.gen(Poly2(0b1100)), a + b);
assert_eq!(g.gen(Poly2(0b1100)), a - b);
assert_eq!(g.gen(Poly2(0)), a + a);
assert_eq!(g.gen(Poly2(0)), b + b);
}
#[test]
fn gf2e_multiply_works() {
let p = Poly2::new(0b10011);
let g = GenGF2E::new(4, p);
let a = g.gen(Poly2(0b1010));
let b = g.gen(Poly2(0b0110));
assert_eq!(g.gen(Poly2(0b1001)), a * b);
}
fn _inverse_works() {
let p = Poly2::new(0b1011);
let g = GenGF2E::new(3, p);
let a = g.gen(Poly2(0b10));
assert_eq!(Some(0b101), _inverse(a.0, p.0));
}
#[test]
fn gf2_inverse_works() {
let p = Poly2::new(0b100011011);
let g = GenGF2E::new(8, p);
let a = g.gen(Poly2(0b10000000));
assert_eq!(Some(0b10000011), gf2_inverse(a.0, p.0, 8));
let p = Poly2::new(0b100011011);
let g = GenGF2E::new(8, p);
let a = g.gen(Poly2(0b10010101));
assert_eq!(Some(0b10001010), gf2_inverse(a.0, p.0, 8));
let p = Poly2::new(0b1011);
let g = GenGF2E::new(3, p);
let a = g.gen(Poly2(0b10));
assert_eq!(Some(0b101), gf2_inverse(a.0, p.0, 3));
}
#[test]
fn gf2e_inverse_works() {
let p = Poly2::new(0b1011);
let g = GenGF2E::new(3, p);
let a = g.gen(Poly2(0b10));
assert_eq!(Some(g.gen(Poly2(0b101))), a.inverse());
let p = Poly2::new(0b10011);
let g = GenGF2E::new(4, p);
let a = g.gen(Poly2(0b10));
assert_eq!(Some(g.gen(Poly2(0b1001))), a.inverse());
}
#[test]
fn gf2_inverse_primitive_works() {
let p = Poly2::new(0b1011);
let g = GenGF2E::new(3, p);
let a = g.gen(Poly2(0b10));
assert_eq!(Some(0b1000000), gf2_inverse_primitive(a.0, 3));
let p = Poly2::new(0b10011);
let g = GenGF2E::new(4, p);
let a = g.gen(Poly2(0b10));
assert_eq!(Some(1 << 14), gf2_inverse_primitive(a.0, 4));
}
}