use crate::field::quad_mersenne31::QuadMersenne31;
use crate::field::{Elem, FieldBuffer};
use crate::kernel::Backend;
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
use crate::kernel::backend;
use crate::kernel::{prime, scalar};
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
const VECTOR_MUL_MIN_BYTES: usize = 32;
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
const VECTOR_ADD_MIN_BYTES: usize = 64;
#[inline]
#[must_use]
const fn canon(x: u32) -> u32 {
let s = (x & crate::field::mersenne31::MODULUS) + (x >> 31);
if s >= crate::field::mersenne31::MODULUS {
s - crate::field::mersenne31::MODULUS
} else {
s
}
}
#[inline]
#[must_use]
#[allow(clippy::cast_possible_truncation)]
const fn raw_mul(a: u32, b: u32) -> u32 {
let prod = (a as u64) * (b as u64);
let s = ((prod as u32) & crate::field::mersenne31::MODULUS) + (prod >> 31) as u32;
if s >= crate::field::mersenne31::MODULUS {
s - crate::field::mersenne31::MODULUS
} else {
s
}
}
#[inline]
#[must_use]
const fn raw_add(a: u32, b: u32) -> u32 {
let s = a + b;
if s >= crate::field::mersenne31::MODULUS {
s - crate::field::mersenne31::MODULUS
} else {
s
}
}
#[inline]
#[must_use]
const fn raw_sub(ac: u32, bd: u32) -> u32 {
let s = ac
.wrapping_sub(bd)
.wrapping_add(crate::field::mersenne31::MODULUS);
if s >= crate::field::mersenne31::MODULUS {
s - crate::field::mersenne31::MODULUS
} else {
s
}
}
#[inline]
#[must_use]
const fn qmul(ar: u32, ai: u32, br: u32, bi: u32) -> Elem<QuadMersenne31> {
let ac = raw_mul(ar, br);
let bd = raw_mul(ai, bi);
let ad = raw_mul(ar, bi);
let bc = raw_mul(ai, br);
Elem::<QuadMersenne31>::from_raw([raw_sub(ac, bd), raw_add(ad, bc)])
}
#[inline]
pub(crate) fn backend_report() -> Backend {
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
{
match backend() {
#[cfg(feature = "simd512")]
Backend::V4x => Backend::V4x,
#[cfg(not(feature = "simd512"))]
Backend::V4x => Backend::V3,
Backend::V3GfniCrypto | Backend::V3 => Backend::V3,
_ => Backend::Scalar,
}
}
#[cfg(not(all(feature = "simd", target_arch = "x86_64")))]
{
Backend::Scalar
}
}
#[inline]
pub(crate) fn has_vector_elementwise() -> bool {
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
{
matches!(
backend(),
Backend::V4x | Backend::V3GfniCrypto | Backend::V3
)
}
#[cfg(not(all(feature = "simd", target_arch = "x86_64")))]
{
false
}
}
#[inline]
pub(crate) fn vector_elementwise_min_bytes() -> usize {
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
{
if matches!(
backend(),
Backend::V4x | Backend::V3GfniCrypto | Backend::V3
) {
VECTOR_MUL_MIN_BYTES
} else {
0
}
}
#[cfg(not(all(feature = "simd", target_arch = "x86_64")))]
{
0
}
}
pub(crate) fn add_assign(dst: &mut [u8], src: &[u8]) {
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
if matches!(
backend(),
Backend::V4x | Backend::V3GfniCrypto | Backend::V3
) && dst.len() >= VECTOR_ADD_MIN_BYTES
{
crate::kernel::x86::quad_mersenne31::add_assign_avx2(
crate::kernel::x86_v3_token(),
dst,
src,
);
return;
}
for (d, s) in dst.chunks_exact_mut(8).zip(src.chunks_exact(8)) {
let a = QuadMersenne31::decode(d).to_raw();
let b = QuadMersenne31::decode(s).to_raw();
QuadMersenne31::encode(
d,
Elem::<QuadMersenne31>::from_raw([
raw_add(canon(a[0]), canon(b[0])),
raw_add(canon(a[1]), canon(b[1])),
]),
);
}
}
pub(crate) fn sub_assign(dst: &mut [u8], src: &[u8]) {
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
if matches!(
backend(),
Backend::V4x | Backend::V3GfniCrypto | Backend::V3
) && dst.len() >= VECTOR_ADD_MIN_BYTES
{
crate::kernel::x86::quad_mersenne31::sub_assign_avx2(
crate::kernel::x86_v3_token(),
dst,
src,
);
return;
}
debug_assert_eq!(dst.len(), src.len());
for (d, s) in dst.chunks_exact_mut(8).zip(src.chunks_exact(8)) {
let a = QuadMersenne31::decode(d).to_raw();
let b = QuadMersenne31::decode(s).to_raw();
let br = canon(b[0]);
let bi = canon(b[1]);
let ar = canon(a[0]);
let ai = canon(a[1]);
QuadMersenne31::encode(
d,
Elem::<QuadMersenne31>::from_raw([
raw_add(
ar,
if br == 0 {
0
} else {
crate::field::mersenne31::MODULUS - br
},
),
raw_add(
ai,
if bi == 0 {
0
} else {
crate::field::mersenne31::MODULUS - bi
},
),
]),
);
}
}
pub(crate) fn mul_add(dst: &mut [u8], coeff: Elem<QuadMersenne31>, src: &[u8]) {
#[cfg(all(feature = "simd512", target_arch = "x86_64"))]
if matches!(backend(), Backend::V4x) && dst.len() >= VECTOR_MUL_MIN_BYTES {
crate::kernel::x86::quad_mersenne31::mul_add_avx512(
crate::kernel::x86_v4_token(),
dst,
coeff,
src,
);
return;
}
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
if matches!(
backend(),
Backend::V4x | Backend::V3GfniCrypto | Backend::V3
) && dst.len() >= VECTOR_MUL_MIN_BYTES
{
crate::kernel::x86::quad_mersenne31::mul_add_avx2(
crate::kernel::x86_v3_token(),
dst,
coeff,
src,
);
return;
}
let cr = canon(coeff.to_raw()[0]);
let ci = canon(coeff.to_raw()[1]);
if cr == 0 && ci == 0 {
return;
}
if cr == 1 && ci == 0 {
add_assign(dst, src);
return;
}
for (d, s) in dst.chunks_exact_mut(8).zip(src.chunks_exact(8)) {
let a = QuadMersenne31::decode(d).to_raw();
let b = QuadMersenne31::decode(s).to_raw();
let prod = qmul(canon(b[0]), canon(b[1]), cr, ci).to_raw();
QuadMersenne31::encode(
d,
Elem::<QuadMersenne31>::from_raw([
raw_add(canon(a[0]), prod[0]),
raw_add(canon(a[1]), prod[1]),
]),
);
}
}
pub(crate) fn mul_assign(dst: &mut [u8], coeff: Elem<QuadMersenne31>) {
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
if matches!(
backend(),
Backend::V4x | Backend::V3GfniCrypto | Backend::V3
) && dst.len() >= VECTOR_MUL_MIN_BYTES
{
crate::kernel::x86::quad_mersenne31::mul_assign_avx2(
crate::kernel::x86_v3_token(),
dst,
coeff,
);
return;
}
let cr = canon(coeff.to_raw()[0]);
let ci = canon(coeff.to_raw()[1]);
if cr == 0 && ci == 0 {
dst.fill(0);
return;
}
if cr == 1 && ci == 0 {
return;
}
for d in dst.chunks_exact_mut(8) {
let a = QuadMersenne31::decode(d).to_raw();
QuadMersenne31::encode(d, qmul(canon(a[0]), canon(a[1]), cr, ci));
}
}
pub(crate) fn mul_into(dst: &mut [u8], coeff: Elem<QuadMersenne31>, src: &[u8]) {
#[cfg(all(feature = "simd512", target_arch = "x86_64"))]
if matches!(backend(), Backend::V4x) && dst.len() >= VECTOR_MUL_MIN_BYTES {
crate::kernel::x86::quad_mersenne31::mul_into_avx512(
crate::kernel::x86_v4_token(),
dst,
coeff,
src,
);
return;
}
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
if matches!(
backend(),
Backend::V4x | Backend::V3GfniCrypto | Backend::V3
) && dst.len() >= VECTOR_MUL_MIN_BYTES
{
crate::kernel::x86::quad_mersenne31::mul_into_avx2(
crate::kernel::x86_v3_token(),
dst,
coeff,
src,
);
return;
}
let cr = canon(coeff.to_raw()[0]);
let ci = canon(coeff.to_raw()[1]);
if cr == 0 && ci == 0 {
dst.fill(0);
return;
}
for (d, s) in dst.chunks_exact_mut(8).zip(src.chunks_exact(8)) {
let b = QuadMersenne31::decode(s).to_raw();
QuadMersenne31::encode(d, qmul(canon(b[0]), canon(b[1]), cr, ci));
}
}
pub(crate) fn mul_elementwise(dst: &mut [u8], a: &[u8], b: &[u8]) {
debug_assert_eq!(dst.len(), a.len());
debug_assert_eq!(dst.len(), b.len());
#[cfg(all(feature = "simd512", target_arch = "x86_64"))]
if matches!(backend(), Backend::V4x) && dst.len() >= VECTOR_MUL_MIN_BYTES {
crate::kernel::x86::quad_mersenne31::mul_elementwise_avx512(
crate::kernel::x86_v4_token(),
dst,
a,
b,
);
return;
}
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
if matches!(
backend(),
Backend::V4x | Backend::V3GfniCrypto | Backend::V3
) && dst.len() >= VECTOR_MUL_MIN_BYTES
{
crate::kernel::x86::quad_mersenne31::mul_elementwise_avx2(
crate::kernel::x86_v3_token(),
dst,
a,
b,
);
return;
}
for (d, x, y) in dst
.chunks_exact_mut(8)
.zip(a.chunks_exact(8))
.zip(b.chunks_exact(8))
.map(|((d, x), y)| (d, x, y))
{
let xa = QuadMersenne31::decode(x).to_raw();
let xb = QuadMersenne31::decode(y).to_raw();
QuadMersenne31::encode(
d,
qmul(canon(xa[0]), canon(xa[1]), canon(xb[0]), canon(xb[1])),
);
}
}
pub(crate) fn mul_elementwise_assign(dst: &mut [u8], src: &[u8]) {
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
if matches!(
backend(),
Backend::V4x | Backend::V3GfniCrypto | Backend::V3
) && dst.len() >= VECTOR_MUL_MIN_BYTES
{
crate::kernel::x86::quad_mersenne31::mul_elementwise_assign_avx2(
crate::kernel::x86_v3_token(),
dst,
src,
);
return;
}
scalar::mul_elementwise_assign::<QuadMersenne31>(dst, src);
}
pub(crate) fn add_assign_scalar(dst: &mut [u8], value: Elem<QuadMersenne31>) {
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
if matches!(
backend(),
Backend::V4x | Backend::V3GfniCrypto | Backend::V3
) && dst.len() >= VECTOR_ADD_MIN_BYTES
{
crate::kernel::x86::quad_mersenne31::add_assign_scalar_avx2(
crate::kernel::x86_v3_token(),
dst,
value,
);
return;
}
prime::add_assign_scalar::<QuadMersenne31>(dst, value);
}
pub(crate) fn sub_assign_scalar(dst: &mut [u8], value: Elem<QuadMersenne31>) {
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
if matches!(
backend(),
Backend::V4x | Backend::V3GfniCrypto | Backend::V3
) && dst.len() >= VECTOR_ADD_MIN_BYTES
{
crate::kernel::x86::quad_mersenne31::sub_assign_scalar_avx2(
crate::kernel::x86_v3_token(),
dst,
value,
);
return;
}
prime::sub_assign_scalar::<QuadMersenne31>(dst, value);
}