use crate::{ GaloisField };
use crate::tables::mull::{lmull,rmull};
use num::{One,Zero};
use std::convert::TryInto;
struct FullMulLUT<G> where G : GaloisField {
table : Vec<G::E>,
}
impl<G> FullMulLUT<G>
where G : GaloisField, G::E : Into<usize>
{
fn new(f : &G) -> FullMulLUT<G> {
let zero = G::E::zero();
let one = G::E::one();
let max : usize = (1 << (f.order() as usize)) - 1;
let mut v = Vec::<G::E>::with_capacity((max + 1) * (max + 1));
let mut a = zero;
let mut b;
for _row in 0..=max {
b = zero;
for _col in 0..=max {
let prod = f.mul(a,b);
v.push(prod);
b = b + one;
}
a = a + one;
}
FullMulLUT::<G> { table : v }
}
#[inline(always)]
fn mul(&self, a : G::E, b : G::E) -> G::E {
let index : usize = (a.into() << G::ORDER) + b.into();
self.table[index]
}
}
fn fill_inverse<T>(f : & T,
v : &mut Vec<T::E>, max : usize)
where T : GaloisField
{
let mut elem = T::E::zero();
v.push(elem);
for _count in 1..=max {
elem = elem + T::E::one();
v.push(f.inv(elem));
}
}
#[doc(hidden)]
pub struct F4_0x13 {
mul_lut : FullMulLUT::<crate::F4>,
inv_lut : Vec<u8>,
}
impl GaloisField for F4_0x13 {
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 { 3 }
fn full_poly(&self) -> u8 { 19 }
fn mul(&self, a : Self::E, b : Self::E) -> Self::E {
self.mul_lut.mul(a,b)
}
fn inv(&self, a : Self::E) -> Self::E
{
self.inv_lut[a as usize]
}
}
pub fn new_gf4_0x13() -> F4_0x13 {
let f = crate::new_gf4(19,3);
let mut inv = Vec::<u8>::with_capacity(16);
fill_inverse(&f, &mut inv, 15);
F4_0x13 {
mul_lut : FullMulLUT::<crate::F4>::new(&f),
inv_lut : inv,
}
}
struct BigLogExpTables<G> where G : GaloisField {
log : Vec<G::SEE>,
exp : Vec<G::E>,
exp_entry : *const G::E,
}
impl<G> BigLogExpTables<G>
where G : GaloisField,
G::E : Into<usize>,
G::E : Into<G::SEE>,
G::SEE : Into<isize>,
G::E : std::fmt::Debug
{
fn new(f : &G, g : G::E ) -> BigLogExpTables<G> {
let log_size = 1 << (G::ORDER as usize);
let see_log_size : G::SEE = G::SEE::one() << (G::ORDER as usize);
let exp_size = log_size * 4;
let exp_entry;
let zero = G::SEE::zero();
let mut exp : Vec<G::E> = Vec::<_>::with_capacity(exp_size);
let mut log : Vec<G::SEE> = vec![zero ; log_size];
for _ in 0..log_size * 2 {
exp.push(G::E::zero());
}
unsafe {
exp_entry = exp.as_ptr().offset(log_size as isize * 2);
}
let mut i : usize = 0;
exp.push(G::E::one());
log[i] = G::SEE::zero() - see_log_size;
exp.push(g);
let usize_g : usize = g.into();
log[i + usize_g] = G::SEE::one();
i += 2;
let mut ei = G::SEE::one() + G::SEE::one();
let mut p = g; loop {
if p == G::E::one() {
panic!("{} is not a generator for this field", g)
}
p = f.mul(p,g);
exp.push(p);
let usize_p : usize = p.into();
log[usize_p] = ei;
i += 1; ei = ei + G::SEE::one();
if i == log_size { break }
}
assert_eq!(p, G::E::one());
for _ in 0..log_size-1 { p = f.mul(p,g);
exp.push(p);
}
assert_eq!(p, G::E::one());
exp.push( G::E::zero() );
assert_eq!(exp_size, exp.len());
BigLogExpTables::<G> { log, exp, exp_entry }
}
#[inline(always)]
fn mul(&self, a : G::E, b : G::E) -> G::E
{
let usize_a : usize = a.into();
let usize_b : usize = b.into();
let log_a : isize;
let log_b : isize;
unsafe {
log_a = (*self.log.get_unchecked(usize_a)).into();
log_b = (*self.log.get_unchecked(usize_b)).into();
*(self.exp_entry.offset(log_a + log_b))
}
}
fn inv(&self, a : G::E) -> G::E {
let log_top = (1 << (G::ORDER as usize)) - 1;
let usize_a : usize = a.into();
let log_a : isize;
unsafe {
log_a = (*self.log.get_unchecked(usize_a)).into();
*(self.exp_entry.offset(log_top - log_a))
}
}
fn pow(&self, a : G::E, b : G::EE) -> G::E
where G::EE : Into<usize>
{
let log_top = (1 << (G::ORDER as usize)) - 1;
let usize_a : usize = a.into();
let usize_b : usize = b.into();
let isize_b : isize = usize_b as isize;
let mut log_a : isize;
unsafe {
log_a = (*self.log.get_unchecked(usize_a)).into();
log_a = (log_a * isize_b) % log_top;
*(self.exp_entry.offset(log_a))
}
}
}
#[doc(hidden)]
pub struct F8_0x11b {
tables : BigLogExpTables::<crate::F8>,
}
impl GaloisField for F8_0x11b {
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 { 0x1b }
fn full_poly(&self) -> u16 { 0x11b }
fn mul(&self, a : Self::E, b : Self::E) -> Self::E {
self.tables.mul(a,b)
}
fn inv(&self, a : Self::E) -> Self::E
{
self.tables.inv(a)
}
fn pow(&self, a : Self::E, b : Self::EE) -> Self::E {
self.tables.pow(a,b)
}
}
pub fn new_gf8_0x11b() -> F8_0x11b {
let f = crate::new_gf8(0x11b,0x1b);
let this = F8_0x11b { tables : BigLogExpTables::<crate::F8>::new(&f, 3),
};
assert_eq!(this.tables.log[0], -256);
this
}
struct BytewiseReduceTable<G> where G : GaloisField {
reduce : Vec<G::E>,
}
impl<G> BytewiseReduceTable<G>
where G : GaloisField,
G::E : Into<usize>,
G::E : std::fmt::Debug
{
fn new(f : &G) -> BytewiseReduceTable<G>
where
G::E : std::convert::TryFrom<G::EE>,
G::E : Into<usize>
{
let mut reduce = Vec::<G::E>::with_capacity(256);
let mut i = G::EE::zero();
let delta = G::EE::one() << G::ORDER.into();
for _ in 0..=254 {
let add = G::mod_reduce(i, f.full_poly());
reduce.push(add);
i = i + delta; }
reduce.push(G::mod_reduce(i, f.full_poly()));
assert_eq!(reduce.len(), 256);
BytewiseReduceTable::<G> { reduce }
}
#[inline(always)]
fn mod_shift_left_8(&self, a : G::E) -> G::E {
let usize_a : usize = a.into();
let shr = (G::ORDER as usize) - 8;
let mask : G::E = unsafe {
*self.reduce.get_unchecked(usize_a >> shr)
};
if G::ORDER <= 8 { mask } else { (a << 8) ^ mask }
}
fn mod_reduce_bytewise(&self, mut a : G::EE) -> G::E
where G::E : Into<G::EE>,
G::EE : std::convert::TryInto<G::E>,
G::E : Into<usize>,
G::EE : Into<usize> {
let mut steps = G::ORDER as usize >> 3;
let shr = (G::ORDER as usize) * 2 - 8;
let half_shift = G::ORDER as usize;
while steps > 0 {
let top_byte = a >> shr;
let usize_byte : usize = top_byte.into();
let mask : G::E = self.reduce[usize_byte];
let xmask : G::EE = mask.into();
a = (a << 8) ^ (xmask << half_shift);
steps -= 1;
}
a = a >> half_shift;
a.try_into().unwrap_or_else(|_| panic!())
}
}
#[doc(hidden)]
pub struct F16_0x1002b {
reduce : BytewiseReduceTable::<crate::F16>,
inv : Vec<u16>,
}
impl GaloisField for F16_0x1002b {
type E = u16;
type EE = u32;
type SEE = i32;
const ORDER : u16 = 16;
const POLY_BIT : u32 = 0x1_0000;
const FIELD_MASK : u16 = 0xffff;
const HIGH_BIT : u16 = 0x8000;
fn poly(&self) -> u16 { 0x2b }
fn full_poly(&self) -> u32 { 0x1002b }
fn mul(&self, a : Self::E, b : Self::E) -> Self::E {
let a3 : u8 = ((a >> 12) as u8) & 0x0f;
let a2 : u8 = ((a >> 8) as u8) & 0x0f;
let a1 : u8 = ((a >> 4) as u8) & 0x0f;
let a0 : u8 = ((a ) as u8) & 0x0f;
let b1 : u8 = ((b >> 8) & 0x00ff) as u8;
let b0 : u8 = ((b ) & 0x00ff) as u8;
let mut c : u16;
c = lmull(b1, a3) ^ rmull(b1, a2);
c = self.reduce.mod_shift_left_8(c);
c = c ^ lmull(b0, a3) ^ rmull(b0, a2);
c = c ^ lmull(b1, a1) ^ rmull(b1, a0);
c = self.reduce.mod_shift_left_8(c);
c = c ^ lmull(b0, a1) ^ rmull(b0, a0);
c
}
fn inv(&self, a : Self::E) -> Self::E
{
self.inv[a as usize]
}
}
pub fn new_gf16_0x1002b() -> F16_0x1002b {
let f = crate::new_gf16(0x1002b,0x2b);
let mut inv = Vec::<u16>::with_capacity(0x10000);
fill_inverse(&f, &mut inv, 0xffff);
let reduce = BytewiseReduceTable::<crate::F16>::new(&f);
F16_0x1002b {
reduce, inv
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{new_gf4, new_gf8, new_gf16};
use crate::{F4, F8, F16};
#[test]
fn test_f4_0x13_mul_conformance() {
let f4 = new_gf4(19,3);
let f4_0x13 = new_gf4_0x13();
let mut fails = 0;
for i in 0..16 {
for j in 0..16 {
if f4.mul(i,j) != f4_0x13.mul(i,j) {
fails += 1;
}
}
}
assert_eq!(fails, 0);
}
#[test]
fn test_f4_0x13_inv_conformance() {
let f4 = new_gf4(19,3);
let f4_0x13 = new_gf4_0x13();
let mut fails = 0;
for i in 0..16 {
if f4.inv(i) != f4_0x13.inv(i) {
fails += 1;
}
}
assert_eq!(fails, 0);
}
#[test]
fn test_f8_0x11b_mul_conformance() {
let f8 = new_gf8(0x11b,0x1b);
let f8_0x11b = new_gf8_0x11b();
let mut fails = 0;
for i in 0..=255 {
for j in 0..=255 {
if f8.mul(i,j) != f8_0x11b.mul(i,j) {
fails += 1;
}
}
}
assert_eq!(fails, 0);
}
#[test]
fn test_f8_0x11b_inv_conformance() {
let f8 = new_gf8(0x11b,0x1b);
let f8_0x11b = new_gf8_0x11b();
let mut fails = 0;
for i in 0..=255 {
if f8.inv(i) != f8_0x11b.inv(i) {
fails += 1;
}
}
assert_eq!(fails, 0);
}
#[test]
fn test_f8_0x11b_pow_conformance() {
let f8 = new_gf8(0x11b,0x1b);
let f8_0x11b = new_gf8_0x11b();
let mut fails = 0;
assert_eq!(f8_0x11b.pow(0,0), 1);
for i in 0..=255 {
for j in 0..=1024 { if f8.pow(i,j) != f8_0x11b.pow(i,j) {
eprintln!("Failing for power {} ** {}", i, j);
eprintln!("Reference: {}, Got: {}",
f8.pow(i,j),
f8_0x11b.pow(i,j)
);
fails += 1;
}
}
}
assert_eq!(fails, 0);
}
#[test]
fn test_f16_0x1002b_mul_conformance_limited1() {
let f16 = new_gf16(0x1002b, 0x2b);
let f16_0x1002b = new_gf16_0x1002b();
let mut fails = 0;
for i in 0u16..=255 {
for j in 0u16..=255 {
if f16.mul(i,j) != f16_0x1002b.mul(i,j) {
eprintln!("Failed mul({} * {})", i, j);
assert_eq!(f16.mul(i,j), f16_0x1002b.mul(i,j),
"(ref vs good");
fails += 1;
}
}
}
assert_eq!(fails, 0);
}
#[test]
fn test_f16_0x1002b_mul_conformance_limited2() {
let f16 = new_gf16(0x1002b, 0x2b);
let f16_0x1002b = new_gf16_0x1002b();
let mut fails = 0;
for i in 0u16..=511 {
for j in 0u16..=255 {
if f16.mul(i,j) != f16_0x1002b.mul(i,j) {
eprintln!("Failed mul({} * {})", i, j);
assert_eq!(f16.mul(i,j), f16_0x1002b.mul(i,j),
"(ref vs good");
fails += 1;
}
}
}
assert_eq!(fails, 0);
}
#[test]
fn test_f16_0x1002b_mul_conformance_limited3() {
let f16 = new_gf16(0x1002b, 0x2b);
let f16_0x1002b = new_gf16_0x1002b();
let mut fails = 0;
for i in 0u16..=255 {
for j in 0u16..=511 {
if f16.mul(i,j) != f16_0x1002b.mul(i,j) {
eprintln!("Failed mul({} * {})", i, j);
assert_eq!(f16.mul(i,j), f16_0x1002b.mul(i,j),
"(ref vs good");
fails += 1;
}
}
}
assert_eq!(fails, 0);
}
#[test]
fn test_f16_0x1002b_mul_conformance_limited4() {
let f16 = new_gf16(0x1002b, 0x2b);
let f16_0x1002b = new_gf16_0x1002b();
let mut fails = 0;
for i in 0u16..=511 {
for j in 0u16..=511 {
if f16.mul(i,j) != f16_0x1002b.mul(i,j) {
eprintln!("Failed mul({} * {})", i, j);
assert_eq!(f16.mul(i,j), f16_0x1002b.mul(i,j),
"(ref vs good");
fails += 1;
}
}
}
assert_eq!(fails, 0);
}
#[test]
fn test_f16_0x1002b_mul_conformance_limited5() {
let f16 = new_gf16(0x1002b, 0x2b);
let f16_0x1002b = new_gf16_0x1002b();
let mut fails = 0;
for i in 0u16..=1025 {
for j in 0u16..=255 {
if f16.mul(i,j) != f16_0x1002b.mul(i,j) {
eprintln!("Failed mul({} * {})", i, j);
assert_eq!(f16.mul(i,j), f16_0x1002b.mul(i,j),
"(ref vs good");
fails += 1;
}
}
}
assert_eq!(fails, 0);
}
#[test]
fn test_f16_0x1002b_mul_conformance_limited6() {
let f16 = new_gf16(0x1002b, 0x2b);
let f16_0x1002b = new_gf16_0x1002b();
let mut fails = 0;
for i in 0u16..=255 {
for j in 0u16..=1025 {
if f16.mul(i,j) != f16_0x1002b.mul(i,j) {
eprintln!("Failed mul({} * {})", i, j);
assert_eq!(f16.mul(i,j), f16_0x1002b.mul(i,j),
"(ref vs good");
fails += 1;
}
}
}
assert_eq!(fails, 0);
}
#[test]
fn test_f16_0x1002b_mul_conformance() {
let f16 = new_gf16(0x1002b, 0x2b);
let f16_0x1002b = new_gf16_0x1002b();
let mut fails = 0;
for i in 0u16..=1023 {
for j in 0u16..=1023 {
if f16.mul(i,j) != f16_0x1002b.mul(i,j) {
eprintln!("Failed mul({} * {})", i, j);
assert_eq!(f16.mul(i,j), f16_0x1002b.mul(i,j),
"(ref vs good");
fails += 1;
}
}
}
assert_eq!(fails, 0);
}
#[test]
fn test_f16_0x1002b_inv_conformance() {
let f16 = new_gf16(0x1002b, 0x2b);
let f16_0x1002b = new_gf16_0x1002b();
let mut fails = 0;
for i in 0..=65535 {
if f16.inv(i) != f16_0x1002b.inv(i) {
fails += 1;
}
}
assert_eq!(fails, 0);
}
#[test]
fn test_bytewise_reduce_table() {
let f = new_gf8(0x11b,0x1b);
let reduce = BytewiseReduceTable::<F8>::new(&f);
let mut fails = 0;
for i in 0u16..=65535 {
let ref_res : u8 = F8::mod_reduce(i,0x11b);
let bytewise_res : u8 = reduce.mod_reduce_bytewise(i);
if ref_res != bytewise_res { fails += 1}
}
assert_eq!(fails, 0);
}
#[test]
fn test_mod_shift_left_8() {
let f = new_gf8(0x11b,0x1b);
let reduce = BytewiseReduceTable::<F8>::new(&f);
let ref_res : u8 = F8::mod_reduce(0xbeef,0x11b);
let part : u8 = reduce.mod_shift_left_8(0xbe);
let shift_res = part ^ 0xef;
assert_eq!(ref_res, shift_res);
}
}