#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct Affine8 {
matrix: u64,
add: u8,
}
const IDENTITY_MATRIX: u64 = 0x0102_0408_1020_4080;
const ONES_STEP_8: u64 = 0x0101_0101_0101_0101;
const MSBS_STEP_8: u64 = 0x8080_8080_8080_8080;
impl Affine8 {
pub const IDENTITY: Self = Self::new(IDENTITY_MATRIX, 0);
pub const NOT: Self = Self::new(IDENTITY_MATRIX, 0xFF);
pub const REVERSE: Self = Self::new(0x8040_2010_0804_0201, 0);
pub const PARITY: Self = Self::new(u64::MAX, 0);
pub const ZERO: Self = Self::new(0, 0);
#[inline]
#[must_use]
pub const fn new(matrix: u64, add: u8) -> Self {
Self { matrix, add }
}
#[inline]
#[must_use]
pub const fn from_rows(rows: [u8; 8], add: u8) -> Self {
let mut bytes = [0u8; 8];
let mut i = 0;
while i < 8 {
bytes[7 - i] = rows[i];
i += 1;
}
Self::new(u64::from_le_bytes(bytes), add)
}
#[inline]
#[must_use]
pub const fn matrix(self) -> u64 {
self.matrix
}
#[inline]
#[must_use]
pub const fn add(self) -> u8 {
self.add
}
#[inline]
#[must_use]
pub const fn row(self, bit: usize) -> u8 {
self.matrix.to_le_bytes()[7 - bit]
}
#[inline]
#[must_use]
pub const fn is_linear(self) -> bool {
self.add == 0
}
#[inline]
#[must_use]
pub const fn shl(n: u32) -> Self {
if n >= 8 {
Self::ZERO
} else {
Self::new(IDENTITY_MATRIX >> (8 * n), 0)
}
}
#[inline]
#[must_use]
pub const fn shr(n: u32) -> Self {
if n >= 8 {
Self::ZERO
} else {
Self::new(IDENTITY_MATRIX << (8 * n), 0)
}
}
#[inline]
#[must_use]
pub const fn sra(n: u32) -> Self {
if n >= 8 {
return Self::new(MSBS_STEP_8, 0);
}
let vacated = !(u64::MAX << (8 * n)) & MSBS_STEP_8;
Self::new((IDENTITY_MATRIX << (8 * n)) | vacated, 0)
}
#[inline]
#[must_use]
pub const fn rotl(n: u32) -> Self {
let n = n % 8;
if n == 0 {
Self::IDENTITY
} else {
Self::new(Self::shl(n).matrix | Self::shr(8 - n).matrix, 0)
}
}
#[inline]
#[must_use]
pub const fn rotr(n: u32) -> Self {
Self::rotl((8 - n % 8) % 8)
}
#[inline]
#[must_use]
pub const fn apply(self, x: u8) -> u8 {
let mut out = 0u8;
let mut i = 0;
while i < 8 {
if (self.row(i) & x).count_ones() & 1 == 1 {
out |= 1 << i;
}
i += 1;
}
out ^ self.add
}
#[inline]
#[must_use]
pub const fn apply8(self, bytes: u64) -> u64 {
let mut out = 0u64;
let mut i = 0;
while i < 8 {
let mut t = bytes & (self.row(i) as u64 * ONES_STEP_8);
t ^= t >> 4;
t ^= t >> 2;
t ^= t >> 1;
out |= (t & ONES_STEP_8) << i;
i += 1;
}
out ^ (self.add as u64 * ONES_STEP_8)
}
#[inline]
#[must_use]
pub const fn tables(self) -> ([u8; 16], [u8; 16]) {
let mut lo = [0u8; 16];
let mut hi = [0u8; 16];
let mut k: u8 = 0;
while k < 16 {
lo[k as usize] = self.apply(k);
hi[k as usize] = self.apply(k << 4) ^ self.add;
k += 1;
}
(lo, hi)
}
#[inline]
#[must_use]
pub const fn then(self, next: Self) -> Self {
let mut rows = [0u8; 8];
let mut i = 0;
while i < 8 {
let selector = next.row(i);
let mut acc = 0u8;
let mut j = 0;
while j < 8 {
if selector >> j & 1 == 1 {
acc ^= self.row(j);
}
j += 1;
}
rows[i] = acc;
i += 1;
}
Self::from_rows(rows, next.apply(self.add))
}
#[inline]
#[must_use]
pub const fn compose(self, first: Self) -> Self {
first.then(self)
}
}