use core::arch::x86_64::{
__m128i, _mm_clmulepi64_si128, _mm_set_epi64x, _mm_setzero_si128, _mm_shuffle_epi32,
_mm_slli_si128, _mm_xor_si128,
};
use core::mem::transmute;
use crate::clmul::basis::TAIL_128;
const LOW_BY_LOW: i32 = 0x00;
const HIGH_BY_HIGH: i32 = 0x11;
const HIGH_BY_LOW: i32 = 0x01;
const LOW_BY_HIGH: i32 = 0x10;
#[inline]
unsafe fn fold_shifted(t0: __m128i, t1: __m128i) -> __m128i {
unsafe {
let tail = _mm_set_epi64x(0, TAIL_128 as i64);
let shifted = _mm_xor_si128(t0, _mm_slli_si128::<8>(t1));
_mm_xor_si128(shifted, _mm_clmulepi64_si128::<HIGH_BY_LOW>(t1, tail))
}
}
#[inline]
pub(crate) fn poly_mul_128(a: u128, b: u128) -> u128 {
unsafe {
let x = transmute::<u128, __m128i>(a);
let y = transmute::<u128, __m128i>(b);
let low = _mm_clmulepi64_si128::<LOW_BY_LOW>(x, y);
let high = _mm_clmulepi64_si128::<HIGH_BY_HIGH>(x, y);
let middle = _mm_xor_si128(
_mm_clmulepi64_si128::<HIGH_BY_LOW>(x, y),
_mm_clmulepi64_si128::<LOW_BY_HIGH>(x, y),
);
transmute::<__m128i, u128>(fold_shifted(low, fold_shifted(middle, high)))
}
}
#[inline]
pub(crate) fn poly_square_128(a: u128) -> u128 {
unsafe {
let x = transmute::<u128, __m128i>(a);
let low = _mm_clmulepi64_si128::<LOW_BY_LOW>(x, x);
let high = _mm_clmulepi64_si128::<HIGH_BY_HIGH>(x, x);
let tail = _mm_set_epi64x(0, TAIL_128 as i64);
let folded = _mm_xor_si128(
_mm_slli_si128::<8>(high),
_mm_clmulepi64_si128::<HIGH_BY_LOW>(high, tail),
);
transmute::<__m128i, u128>(fold_shifted(low, folded))
}
}
#[inline]
pub(crate) fn poly_dot_128(pairs: impl Iterator<Item = (u128, u128)>) -> u128 {
unsafe {
let mut low = _mm_setzero_si128();
let mut high = _mm_setzero_si128();
let mut middle = _mm_setzero_si128();
for (a, b) in pairs {
let x = transmute::<u128, __m128i>(a);
let y = transmute::<u128, __m128i>(b);
low = _mm_xor_si128(low, _mm_clmulepi64_si128::<LOW_BY_LOW>(x, y));
high = _mm_xor_si128(high, _mm_clmulepi64_si128::<HIGH_BY_HIGH>(x, y));
let mixed_x = _mm_xor_si128(x, _mm_shuffle_epi32::<0x4e>(x));
let mixed_y = _mm_xor_si128(y, _mm_shuffle_epi32::<0x4e>(y));
middle = _mm_xor_si128(middle, _mm_clmulepi64_si128::<LOW_BY_LOW>(mixed_x, mixed_y));
}
middle = _mm_xor_si128(middle, _mm_xor_si128(low, high));
transmute::<__m128i, u128>(fold_shifted(low, fold_shifted(middle, high)))
}
}
#[inline]
pub(crate) fn poly_mul_128_by_64(a: u128, b: u64) -> u128 {
unsafe {
let a = transmute::<u128, __m128i>(a);
let b = _mm_set_epi64x(0, b as i64);
let low = _mm_clmulepi64_si128::<LOW_BY_LOW>(a, b);
let middle = _mm_clmulepi64_si128::<HIGH_BY_LOW>(a, b);
transmute::<__m128i, u128>(fold_shifted(low, middle))
}
}