#[cfg(target_feature = "sha3")]
use core::arch::aarch64::veor3q_u64;
use core::arch::aarch64::{
vandq_u64, vdupq_n_u64, veorq_u64, vgetq_lane_u64, vld1q_u64, vtstq_u64,
};
use crate::clmul::gf64::repeated_square;
const PAIRS: usize = 32;
const CHAINS: usize = 4;
const BITS: [[u64; 2]; PAIRS] = {
let mut bits = [[0; 2]; PAIRS];
let mut j = 0;
while j < PAIRS {
bits[j] = [1 << (2 * j), 1 << (2 * j + 1)];
j += 1;
}
bits
};
struct Columns<const K: usize>([[u64; 2]; PAIRS]);
impl<const K: usize> Columns<K> {
const NEW: Self = {
let mut columns = [[0; 2]; PAIRS];
let mut j = 0;
while j < PAIRS {
columns[j] = [
repeated_square(1 << (2 * j), K),
repeated_square(1 << (2 * j + 1), K),
];
j += 1;
}
Self(columns)
};
}
#[inline]
pub(crate) fn square_times<const K: usize>(x: u64) -> u64 {
let columns: &'static [[u64; 2]; PAIRS] = const { &Columns::<K>::NEW.0 };
unsafe {
let x = vdupq_n_u64(x);
let term = |bits: &[u64; 2], column: &[u64; 2]| {
let mask = vtstq_u64(x, vld1q_u64(bits.as_ptr()));
vandq_u64(mask, vld1q_u64(column.as_ptr()))
};
let zero = vdupq_n_u64(0);
let (mut a, mut b, mut c, mut d) = (zero, zero, zero, zero);
for (bits, column) in BITS
.as_chunks::<CHAINS>()
.0
.iter()
.zip(columns.as_chunks::<CHAINS>().0)
{
a = veorq_u64(a, term(&bits[0], &column[0]));
b = veorq_u64(b, term(&bits[1], &column[1]));
c = veorq_u64(c, term(&bits[2], &column[2]));
d = veorq_u64(d, term(&bits[3], &column[3]));
}
#[cfg(target_feature = "sha3")]
let sum = veorq_u64(veor3q_u64(a, b, c), d);
#[cfg(not(target_feature = "sha3"))]
let sum = veorq_u64(veorq_u64(a, b), veorq_u64(c, d));
vgetq_lane_u64::<0>(sum) ^ vgetq_lane_u64::<1>(sum)
}
}
#[cfg(test)]
mod tests {
use proptest::prelude::*;
use super::square_times;
use crate::clmul::gf64::repeated_square;
use crate::clmul::poly_square_64;
const CORNERS: [u64; 5] = [0, 1, u64::MAX, 1 << 63, 0xf << 60];
#[test]
fn the_matrix_product_is_exact_on_the_basis() {
for c in 0..64 {
let x = 1u64 << c;
assert_eq!(square_times::<3>(x), repeated_square(x, 3), "x^{c}");
assert_eq!(square_times::<6>(x), repeated_square(x, 6), "x^{c}");
assert_eq!(square_times::<12>(x), repeated_square(x, 12), "x^{c}");
assert_eq!(square_times::<24>(x), repeated_square(x, 24), "x^{c}");
}
for x in CORNERS {
assert_eq!(square_times::<24>(x), repeated_square(x, 24), "{x:#x}");
}
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(2000))]
#[test]
fn the_matrix_product_matches_repeated_squaring(x: u64) {
let repeated = |k: usize| (0..k).fold(x, |y, _| poly_square_64(y));
prop_assert_eq!(square_times::<3>(x), repeated(3));
prop_assert_eq!(square_times::<6>(x), repeated(6));
prop_assert_eq!(square_times::<12>(x), repeated(12));
prop_assert_eq!(square_times::<24>(x), repeated(24));
}
}
}