use num::{PrimInt,One,Zero};
use std::convert::{TryInto};
pub mod good;
pub mod tables;
pub trait ElementStore : 'static + Copy
+ num_traits::int::PrimInt
+ std::fmt::Display
+ std::fmt::Debug
+ num::FromPrimitive + num::ToPrimitive
{}
impl<T> ElementStore for T
where T : 'static + Copy
+ num_traits::int::PrimInt
+ std::fmt::Display
+ std::fmt::Debug
+ num::FromPrimitive + num::ToPrimitive
{}
pub trait GaloisField
where Self::EE : From<Self::E> {
type E : ElementStore;
type EE : ElementStore; type SEE : ElementStore;
const ORDER : u16;
const HIGH_BIT : Self::E;
const POLY_BIT : Self::EE;
const FIELD_MASK : Self::E;
fn poly(&self) -> Self::E;
fn full_poly(&self) -> Self::EE;
fn add(&self, a : Self::E, b : Self::E) -> Self::E
{
a ^ b
}
fn sub(&self, a : Self::E, b : Self::E) -> Self::E
{
self.add(a, b)
}
fn mul(&self, mut a : Self::E, b : Self::E) -> Self::E
{
let poly = self.poly();
let zero = Self::E::zero();
let one = Self::E::one();
let field_mask : Self::E = Self::FIELD_MASK;
let high_bit : Self::E = Self::HIGH_BIT;
let mut result : Self::E = if b & one != zero {a} else { zero };
let mut bit : Self::E = one + one;
loop {
if a & high_bit != zero {
a = ((a << 1) ^ poly) & field_mask;
} else {
a = a << 1
}
if b & bit != zero {
result = result ^ a
}
bit = bit << 1;
if bit & field_mask == zero { return result }
}
}
fn div(&self, a : Self::E, b : Self::E) -> Self::E
{
self.mul(a, self.inv(b))
}
fn inv(&self, a : Self::E) -> Self::E
{
let (zero, one) = (Self::E::zero(),
Self::E::one());
let mut u : Self::E = self.poly();
let mut v : Self::E = a;
let mut z : Self::E = zero;
let mut g : Self::E = one;
let mut t : Self::E; let mask = self.field_mask();
if a == zero || a == one { return a }
let fixup = if self.order() == 4 { 4 } else { 0 };
let mut i : u32 = 1 + v.leading_zeros() - fixup;
u = (u ^ v << i as usize) & mask;
z = (z ^ g << i as usize) & mask;
while u != one {
if u.leading_zeros() > v.leading_zeros() {
t=u; u=v; v=t;
t=z; z=g; g=t;
} else {
}
i = v.leading_zeros() - u.leading_zeros();
u = (u ^ v << i as usize) & mask;
z = (z ^ g << i as usize) & mask;
}
z
}
fn pow(&self, a : Self::E, mut b : Self::EE) -> Self::E
where Self::E : Into<Self::EE> {
let mut result : Self::E = a;
let zero = Self::EE::zero();
let one = Self::E::one();
let mut mask : Self::EE;
while b >= (Self::FIELD_MASK).into() {
b = b - (Self::FIELD_MASK).into()
}
if b == zero
{ return one }
let clz = b.leading_zeros() as usize;
let bits = zero.leading_zeros() as usize;
mask = Self::EE::one() << (bits - clz - 1);
loop {
mask = mask >> 1;
if mask == zero { break }
result = self.mul(result, result);
if b & mask != zero { result = self.mul(result, a) }
}
result
}
#[doc(hidden)]
fn mull(a : Self::E, b : Self::E) -> Self::EE
where Self::EE: From<Self::E>
{
let ezero = Self::E::zero();
let eezero = Self::EE::zero();
let mut aa : Self::EE = a.into();
let mut res = eezero;
let mut mask = Self::E::one();
loop {
if b & mask != ezero { res = res ^ aa }
mask = mask << 1;
aa = aa << 1;
if aa == eezero { return res }
}
}
#[doc(hidden)]
fn mod_reduce(mut a : Self::EE, mut poly : Self::EE) -> Self::E
where Self::E: std::convert::TryFrom<Self::EE>
{
let eezero = Self::EE::zero();
poly = poly << (Self::ORDER - 1).into();
let mut mask = Self::POLY_BIT << (Self::ORDER - 1).into();
loop {
if a & mask != eezero { a = a ^ poly }
if a < Self::POLY_BIT {
return a.try_into().unwrap_or_else(|_| Self::E::one())
}
mask = mask >> 1;
poly = poly >> 1;
}
}
fn vec_sum_elements(&self, v : &[Self::E]) -> Self::E {
let mut sum = Self::E::zero();
for e in v.iter() {
sum = sum ^ *e;
}
sum
}
fn vec_add_vec_in_place(&self,
dest : &mut [Self::E],
other : &[Self::E] ) {
assert_eq!(dest.len(), other.len());
for (d,o) in dest.iter_mut().zip(other) {
*d = *d ^ *o
}
}
fn vec_add_vecs_giving_other(&self,
dest : &mut [Self::E],
a : &[Self::E],
b : &[Self::E]) {
assert_eq!(dest.len(), a.len());
assert_eq!(a.len(), b.len());
let (mut a_iter, mut b_iter) = (a.iter(), b.iter());
for d in dest.iter_mut() {
*d = *a_iter.next().unwrap() ^ *b_iter.next().unwrap()
}
}
fn vec_cross_product(&self,
dest : &mut [Self::E],
a : &[Self::E],
b : &[Self::E] ) {
assert_eq!(dest.len(), a.len());
assert_eq!(a.len(), b.len());
let (mut a_iter, mut b_iter) = (a.iter(), b.iter());
for d in dest.iter_mut() {
*d = self.mul(*a_iter.next().unwrap(), *b_iter.next().unwrap())
}
}
fn vec_dot_product(&self, a : &[Self::E], b : &[Self::E]) -> Self::E
{
assert_eq!(a.len(), b.len());
let mut sum = Self::E::zero();
for (a_item, b_item) in a.iter().zip(b) {
sum = sum ^ self.mul(*a_item, *b_item);
}
sum
}
fn vec_constant_scale_in_place(&self,
dest : &mut [Self::E],
a : Self::E) {
for d in dest.iter_mut() {
*d = self.mul(*d, a)
}
}
fn vec_fma_in_place(&self,
dest : &mut [Self::E],
a : Self::E,
b : Self::E ) {
for d in dest.iter_mut() {
*d = self.mul(*d, a) ^ b;
}
}
fn high_bit(&self) -> Self::E { Self::HIGH_BIT }
fn order(&self) -> u16 { Self::ORDER }
fn field_mask(&self)-> Self::E { Self::FIELD_MASK }
}
#[derive(Debug)]
pub struct F4 { pub full : u8, pub compact : u8 }
#[derive(Debug)]
pub struct F8 { pub full : u16, pub compact : u8 }
#[derive(Debug)]
pub struct F16 { pub full : u32, pub compact : u16 }
#[derive(Debug)]
pub struct F32 { pub full : u64, pub compact : u32 }
impl GaloisField for F4 {
type E = u8;
type EE = u8;
type SEE = i8;
const ORDER : u16 = 4;
const POLY_BIT : u8 = 0x10;
const FIELD_MASK : u8 = 0x0f;
const HIGH_BIT : u8 = 0x08;
fn poly(&self) -> u8 { self.compact }
fn full_poly(&self) -> u8 { self.full }
}
#[allow(dead_code)]
pub fn new_gf4(full : u8, compact : u8) -> F4 {
F4 {full, compact}
}
impl GaloisField for F8 {
type E = u8;
type EE = u16;
type SEE = i16;
const ORDER : u16 = 8;
const POLY_BIT : u16 = 0x100;
const FIELD_MASK : u8 = 0xff;
const HIGH_BIT : u8 = 0x80;
fn poly(&self) -> u8 { self.compact }
fn full_poly(&self) -> u16 { self.full }
}
#[allow(dead_code)]
pub fn new_gf8(full : u16, compact : u8) -> F8 {
F8 {full, compact}
}
impl GaloisField for F16 {
type E = u16;
type EE = u32;
type SEE = i32;
const ORDER : u16 = 16;
const POLY_BIT : u32 = 0x10000;
const FIELD_MASK : u16 = 0xffff;
const HIGH_BIT : u16 = 0x8000;
fn poly(&self) -> u16 { self.compact }
fn full_poly(&self) -> u32 { self.full }
}
#[allow(dead_code)]
pub fn new_gf16(full : u32, compact : u16) -> F16 {
F16 { full, compact }
}
impl GaloisField for F32 {
type E = u32;
type EE = u64;
type SEE = i64;
const ORDER : u16 = 32;
const POLY_BIT : u64 = 0x100000000;
const FIELD_MASK : u32 = 0xffffffff;
const HIGH_BIT : u32 = 0x80000000;
fn poly(&self) -> u32 { self.compact }
fn full_poly(&self) -> u64 { self.full }
}
#[allow(dead_code)]
pub fn new_gf32(full : u64, compact : u32) -> F32 {
F32 { full, compact }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_gf4_works() {
let f = new_gf4(19, 3);
let t = f.mul(1,1);
assert_eq!(t, 1)
}
#[test]
fn new_gf8_works() {
let f = new_gf8(0x11d, 0x1d);
let t = f.mul(1,1);
assert_eq!(t, 1)
}
#[test]
fn new_gf16_works() {
let f = new_gf16(0x1_002b, 0x2b);
let t = f.mul(1,1);
assert_eq!(t, 1)
}
#[test]
fn new_gf32_works() {
let f = new_gf32(0x1_0000_008d, 0x8d);
let t = f.mul(1,1);
assert_eq!(t, 1)
}
#[test]
fn zero_a_mod() { let obj = new_gf4(19, 3);
let mut failed = 0;
for i in 0..16 {
if obj.mul(0, i) != 0 { failed += 1 }
}
if failed > 0 { panic!("GF(16): failed {}/16 tests of a=0 * b", failed) }
}
#[test]
fn zero_b_mod() { let obj = new_gf4(19, 3);
let mut failed = 0;
for i in 0..16 {
if obj.mul(i, 0) != 0 { failed += 1 }
}
if failed > 0 { panic!("GF(16): failed {}/16 tests of a * b=0", failed) }
}
#[test]
fn one_a_mod() { let obj = new_gf4(19, 3);
let mut failed = 0;
for i in 0..16 {
if obj.mul(i, 1) != i { failed += 1 }
}
if failed > 0 { panic!("GF(16): failed {}/16 tests of a=1 * b", failed) }
}
#[test]
fn one_b_mod() { let obj = new_gf4(19, 3);
let mut failed = 0;
for i in 0..16 {
if obj.mul(1, i) != i { failed += 1 }
}
if failed > 0 { panic!("GF(16): failed {}/16 tests of a * b=1", failed) }
}
#[test]
fn commutative_mod1() {
let mut failed = 0;
let obj = new_gf4(19, 3);
for i in 2..16 {
for j in 2..16 {
let result = obj.mul(i,j);
if result != obj.mul(j, i) { failed += 1 }
}
}
if failed > 0 { panic!("GF(16): failed {}/169 commutativity tests", failed) }
}
#[test]
fn x_times_x1_mod1_straight() {
let obj = new_gf4(19, 3);
let (a, b) = (0b0000_0010, 0b0000_0011);
assert_eq!(obj.mul(a,b), 0b0000_0110, "straight x(x+1)");
}
#[test]
fn xx_times_x1_mod1_straight() {
let obj = new_gf4(19, 3);
let (a, b) = (0b0000_0100, 0b0000_0011);
assert_eq!(obj.mul(a,b), 0b0000_1100, "straight xx(x+1)");
}
#[test]
fn distributive_1() {
let obj = new_gf4(19, 3);
let (a, b) = (0b0000_0011, 0b0000_1010);
let result = obj.mul(a, b);
let check = obj.add(obj.mul(0b0000_0010, b),
obj.mul(0b0000_0001, b));
assert_eq!(result, check, "distributive 1");
}
#[test]
fn distributive_2() {
let obj = new_gf4(19, 3);
let (a, b) = (0b0000_1010, 0b0000_1111);
let result = obj.mul(a, b);
let check = obj.add(obj.mul(0b0000_1000, b),
obj.mul(0b0000_0010, b));
assert_eq!(result, check, "distributive 2");
}
#[test]
fn generator_should_loop () {
let poly_19_known_gs = vec![2u8, 3, 4, 5, 9, 11, 13, 14];
let poly_25_known_gs = vec![2u8, 4, 6, 7, 9, 12, 13, 14];
let poly_31_known_gs = vec![3u8, 5, 6, 7, 9, 10, 11, 14];
for (poly, gens) in [(19u8, poly_19_known_gs),
(25u8, poly_25_known_gs),
(31u8, poly_31_known_gs) ].iter() {
for g in gens {
let obj = new_gf4(*poly, *poly - 16);
assert_eq!(1, obj.pow(*g, 15),
"poly,generator ({}, {}) failed g ** 15 = 1", *poly, *g);
assert_eq!(*g, obj.pow(*g, 16),
"poly,generator ({}, {}) failed g ** 16 = g", *poly, *g);
}
}
}
#[test]
fn mod_power_15() { let obj = new_gf4(19, 3);
for a in 1..=15 {
let direct = obj.pow(a, 15);
let indirect_5 = obj.pow(a, 5);
let indirect_15 = obj.pow(indirect_5, 3);
assert_eq!(direct, indirect_15, "a = {}", a);
}
}
#[test]
fn mod_power_16 () {
let obj = new_gf4(19, 3);
for a in 0..=15 {
let a2nd = obj.pow(a, 2);
let a4th = obj.pow(a, 4);
assert_eq!(a4th, obj.pow(a2nd, 2));
let ans_1 = obj.pow(a4th, 4);
let a8th = obj.mul(a4th, a4th);
let a16th = obj.pow(a8th, 2);
assert_eq!(ans_1, a16th);
}
}
#[test]
fn mod_power_16_directly () {
let obj = new_gf4(19, 3);
for a in 2..=15 {
let a15th = obj.pow(a, 15);
let a16th = obj.pow(a, 16);
assert_eq!(a16th, obj.mul(a, a15th), "fail for pow({},15/16)", a);
}
}
fn _debug_pow() {
eprintln!("Testing 2**b for b 0..=15");
let obj = new_gf4(19, 3);
for i in 0..=16 {
let res = obj.pow(2, i);
eprintln!("2**{} = {}", i, res)
}
}
#[test]
fn test_new_gf8() {
let obj = F8 { full : 0x11b, compact : 0x1b };
let test = obj.mul(1,1);
assert_eq!(test, 1);
}
#[test]
fn new_field_from_new() {
let f = new_gf8(0x11b, 0x1b);
assert_eq!(8, F8::ORDER, "access ORDER associated type");
assert_eq!(0x11b, f.full_poly(), "access full_poly method");
}
#[test]
fn test_multiply_53_ca() {
assert_eq!(1, new_gf8(0x11b, 0x1b).mul(0x53, 0xCA))
}
#[test]
fn test_multiply_00_00() {
assert_eq!(0, new_gf8(0x11b, 0x1b).mul(0x00, 0x00))
}
#[test]
fn test_multiply_00_01() {
assert_eq!(0, new_gf8(0x11b, 0x1b).mul(0x00, 0x01))
}
#[test]
fn test_multiply_01_01() {
assert_eq!(1, new_gf8(0x11b, 0x1b).mul(0x01, 0x01))
}
#[test]
fn test_multiply_01_02() {
assert_eq!(2, new_gf8(0x11b, 0x1b).mul(0x01, 0x02))
}
#[test]
fn test_inv_01() {
assert_eq!(1, new_gf8(0x11b, 0x1b).inv(0x01))
}
#[test]
fn test_inv_53() {
assert_eq!(0xca, new_gf8(0x11b, 0x1b).inv(0x53))
}
#[test]
fn test_inv_ca() {
assert_eq!(0x53, new_gf8(0x11b, 0x1b).inv(0xca))
}
#[test]
fn test_div_01_53() {
assert_eq!(0xca, new_gf8(0x11b, 0x1b).div(1,0x53))
}
#[test]
fn test_div_01_ca() {
assert_eq!(0x53, new_gf8(0x11b, 0x1b).div(1,0xca))
}
#[test]
fn test_pow_3_0() {
assert_eq!(0x01, new_gf8(0x11b, 0x1b).pow(3,0))
}
#[test]
fn test_pow_3_255() {
assert_eq!(0x1, new_gf8(0x11b, 0x1b).pow(3,255))
}
#[test]
fn test_pow_3_254() {
let f = new_gf8(0x11b, 0x1b);
assert_eq!(f.pow(3,254), f.inv(3))
}
#[test]
fn test_pow_7_2() {
let f = new_gf8(0x11b, 0x1b);
assert_eq!(f.pow(7,2), f.mul(7,7))
}
#[test]
fn test_pow_7_3() {
let f = new_gf8(0x11b, 0x1b);
assert_eq!(f.pow(7,3), f.mul(f.pow(7,2),7))
}
#[test]
fn test_pow_0_0() {
let f = new_gf8(0x11b, 0x1b);
assert_eq!(1,f.pow(0,0))
}
#[test]
fn test_div_0_0() {
let f = new_gf8(0x11b, 0x1b);
assert_eq!(0,f.div(0,0))
}
#[test]
fn test_div_i_0() {
let f = new_gf8(0x11b, 0x1b);
for i in 1..=255 {
assert_eq!(0,f.div(i,0))
}
}
#[test]
fn test_pow_257() {
let f = F8 { full : 0x11b, compact : 0x1b };
let a = f.pow(12,254);
let b = f.pow(12,255);
let c = f.pow(12,256);
let d = f.pow(12,257);
assert_eq!(b,f.mul(a,12));
assert_eq!(c,f.mul(b,12));
assert_eq!(d,f.mul(c,12));
}
#[test]
fn test_new_gf16() {
let obj = new_gf16(0x1002b, 0x2b);
let test = obj.mul(1,1);
assert_eq!(test, 1);
}
#[test]
fn test_new_gf32() {
let obj = new_gf32(0x10000008d, 0x8d);
let test = obj.mul(1,1);
assert_eq!(test, 1);
}
#[test]
fn access_assoc_type() {
type F = <F8 as GaloisField>::E;
let _a : F = 1;
assert_eq!(1, std::mem::size_of::<F>());
}
fn test_impl_assoc_type<T>(_f : & T, s : usize)
where T : GaloisField
{
let one : T::E = T::E::one();
let mut _a : T::E = num::NumCast::from(1).unwrap();
_a = _a + one;
assert_eq!(s, std::mem::size_of::<T::E>());
}
#[test]
fn call_impl_assoc_type() {
let f4 = new_gf4(29, 13);
test_impl_assoc_type(&f4, 1);
let f8 = new_gf4(29, 13);
test_impl_assoc_type(&f8, 1);
let f16 = new_gf16(29, 13);
test_impl_assoc_type(&f16, 2);
let f32 = new_gf32(29, 13);
test_impl_assoc_type(&f32, 4);
}
#[test]
fn long_mul_mod_reduce_conformance() {
let f = new_gf4(19, 3);
for a in 0..=15 {
for b in 0..=15 {
let longmul = F4::mull(a,b);
assert_eq!(f.mul(a,b), F4::mod_reduce(longmul, 19));
}
}
}
#[test]
fn test_gf8_inverses() {
let f = new_gf8(0x11b, 0x1b);
for a in 0..=255 {
let inv = f.inv(a);
let invinv = f.inv(inv);
assert_eq!(a, invinv);
}
}
#[test]
fn access_lmull() {
use crate::tables::mull;
assert_eq!(mull::MULL.len(), 4096);
}
}