use super::{poly_mul_128, poly_square_128, portable};
type PowerMap = [[u128; 256]; 16];
const fn power_map(squarings: usize) -> PowerMap {
let mut table = [[0; 256]; 16];
let mut byte = 0;
while byte < 16 {
let mut bit = 0;
while bit < 8 {
let mut image = 1u128 << (8 * byte + bit);
let mut step = 0;
while step < squarings {
image = portable::poly_square_128(image);
step += 1;
}
table[byte][1 << bit] = image;
bit += 1;
}
let mut value = 1usize;
while value < 256 {
let low = 1 << value.trailing_zeros();
table[byte][value] = table[byte][low] ^ table[byte][value ^ low];
value += 1;
}
byte += 1;
}
table
}
static POW_3: PowerMap = power_map(3);
static POW_7: PowerMap = power_map(7);
static POW_14: PowerMap = power_map(14);
static POW_28: PowerMap = power_map(28);
static POW_63: PowerMap = power_map(63);
#[inline]
fn apply(table: &PowerMap, value: u128) -> u128 {
table
.iter()
.zip(value.to_le_bytes())
.fold(0, |acc, (row, byte)| acc ^ row[usize::from(byte)])
}
#[inline]
pub(crate) fn poly_inverse_128(x: u128) -> u128 {
let b2 = poly_mul_128(poly_square_128(x), x);
let b3 = poly_mul_128(poly_square_128(b2), x);
let b6 = poly_mul_128(apply(&POW_3, b3), b3);
let b7 = poly_mul_128(poly_square_128(b6), x);
let b14 = poly_mul_128(apply(&POW_7, b7), b7);
let b28 = poly_mul_128(apply(&POW_14, b14), b14);
let b56 = poly_mul_128(apply(&POW_28, b28), b28);
let b63 = poly_mul_128(apply(&POW_7, b56), b7);
let b126 = poly_mul_128(apply(&POW_63, b63), b63);
let b127 = poly_mul_128(poly_square_128(b126), x);
poly_square_128(b127)
}
#[cfg(test)]
mod tests {
use proptest::prelude::*;
use super::*;
use crate::clmul::basis::{TAIL_128, poly_mul};
#[test]
fn power_maps_match_the_modulus_on_every_basis_vector() {
for (count, table) in [
(3, &POW_3),
(7, &POW_7),
(14, &POW_14),
(28, &POW_28),
(63, &POW_63),
] {
for bit in 0..128 {
let input = 1u128 << bit;
let mut expected = input;
for _ in 0..count {
expected = poly_mul(expected, expected, 128, TAIL_128);
}
assert_eq!(apply(table, input), expected);
}
}
}
proptest! {
#[test]
fn inversion_matches_the_multiplicative_identity(x: u128) {
let inverse = poly_inverse_128(x);
prop_assert_eq!(poly_mul(x, inverse, 128, TAIL_128), u128::from(x != 0));
}
}
#[test]
fn inversion_handles_zero_and_one() {
assert_eq!(poly_inverse_128(0), 0);
assert_eq!(poly_inverse_128(1), 1);
}
}