use core::arch::x86_64::{
__m128i, _mm_clmulepi64_si128, _mm_cvtsi128_si64, _mm_set_epi64x, _mm_setzero_si128,
_mm_shuffle_epi32, _mm_slli_si128, _mm_unpackhi_epi64, _mm_xor_si128,
};
use core::mem::transmute;
use super::basis::{TAIL_64, 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]
pub(super) fn clmul_64x64(a: u64, b: u64) -> u128 {
unsafe {
let a = _mm_set_epi64x(0, a as i64);
let b = _mm_set_epi64x(0, b as i64);
let product = _mm_clmulepi64_si128::<LOW_BY_LOW>(a, b);
let low = _mm_cvtsi128_si64(product) as u64;
let high = _mm_cvtsi128_si64(_mm_unpackhi_epi64(product, product)) as u64;
u128::from(low) | (u128::from(high) << 64)
}
}
#[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))
}
}
#[inline]
unsafe fn fold_64(product: __m128i) -> u64 {
unsafe {
let tail = _mm_set_epi64x(0, TAIL_64 as i64);
let first = _mm_clmulepi64_si128::<HIGH_BY_LOW>(product, tail);
let second = _mm_clmulepi64_si128::<HIGH_BY_LOW>(first, tail);
let acc = _mm_xor_si128(_mm_xor_si128(product, first), second);
_mm_cvtsi128_si64(acc) as u64
}
}
#[inline]
pub(crate) fn poly_mul_64(a: u64, b: u64) -> u64 {
unsafe {
let x = _mm_set_epi64x(0, a as i64);
let y = _mm_set_epi64x(0, b as i64);
fold_64(_mm_clmulepi64_si128::<LOW_BY_LOW>(x, y))
}
}
#[inline]
pub(crate) fn poly_square_64(a: u64) -> u64 {
unsafe {
let x = _mm_set_epi64x(0, a as i64);
fold_64(_mm_clmulepi64_si128::<LOW_BY_LOW>(x, x))
}
}
#[inline]
pub(crate) fn poly_dot_64(pairs: impl Iterator<Item = (u64, u64)>) -> u64 {
unsafe {
let mut acc = _mm_setzero_si128();
for (a, b) in pairs {
let x = _mm_set_epi64x(0, a as i64);
let y = _mm_set_epi64x(0, b as i64);
acc = _mm_xor_si128(acc, _mm_clmulepi64_si128::<LOW_BY_LOW>(x, y));
}
fold_64(acc)
}
}
#[inline]
pub(crate) fn poly_mul_192(a: [u64; 3], b: [u64; 3]) -> [u64; 3] {
unsafe {
let lift = |v: u64| _mm_set_epi64x(0, v as i64);
let [a0, a1, a2] = a.map(lift);
let [b0, b1, b2] = b.map(lift);
let product = |x, y| _mm_clmulepi64_si128::<LOW_BY_LOW>(x, y);
let sum = _mm_xor_si128;
let c0 = product(a0, b0);
let c1 = product(a1, b1);
let c2 = product(a2, b2);
let d01 = product(sum(a0, a1), sum(b0, b1));
let d02 = product(sum(a0, a2), sum(b0, b2));
let d12 = product(sum(a1, a2), sum(b1, b2));
let p1 = sum(d01, sum(c0, c1));
let p2 = sum(d02, sum(sum(c0, c1), c2));
let p3 = sum(d12, sum(c1, c2));
[
fold_64(sum(c0, p3)),
fold_64(sum(sum(p1, p3), c2)),
fold_64(sum(p2, c2)),
]
}
}