use alloc::vec::Vec;
use core::arch::aarch64::{self, int32x4_t, uint32x4_t};
use core::arch::asm;
use core::hint::unreachable_unchecked;
use core::iter::{Product, Sum};
use core::mem::transmute;
use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use p3_field::interleave::{interleave_u32, interleave_u64};
use p3_field::op_assign_macros::{
impl_add_assign, impl_add_base_field, impl_div_methods, impl_mul_base_field, impl_mul_methods,
impl_packed_value, impl_rng, impl_sub_assign, impl_sub_base_field, impl_sum_prod_base_field,
ring_sum,
};
use p3_field::{
Algebra, Field, InjectiveMonomial, PackedField, PackedFieldPow2, PackedValue,
PermutationMonomial, PrimeCharacteristicRing, impl_packed_field_pow_2, uint32x4_mod_add,
uint32x4_mod_sub,
};
use p3_util::reconstitute_from_base;
use rand::Rng;
use rand::distr::{Distribution, StandardUniform};
use super::utils::halve_neon;
use crate::{
BinomialExtensionData, FieldParameters, MontyField31, PackedMontyParameters,
RelativelyPrimePower,
};
const WIDTH: usize = 4;
pub trait MontyParametersNeon {
const PACKED_P: uint32x4_t;
const PACKED_MU: int32x4_t;
}
trait IntoVec<P: PackedMontyParameters>: Copy {
fn into_vec(self) -> uint32x4_t;
}
impl<P: PackedMontyParameters> IntoVec<P> for PackedMontyField31Neon<P> {
#[inline(always)]
fn into_vec(self) -> uint32x4_t {
self.to_vector()
}
}
impl<P: PackedMontyParameters> IntoVec<P> for MontyField31<P> {
#[inline(always)]
fn into_vec(self) -> uint32x4_t {
unsafe { aarch64::vdupq_n_u32(self.value) }
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[repr(transparent)] #[must_use]
pub struct PackedMontyField31Neon<PMP: PackedMontyParameters>(pub [MontyField31<PMP>; WIDTH]);
impl<PMP: PackedMontyParameters> PackedMontyField31Neon<PMP> {
#[inline]
#[must_use]
pub(crate) fn to_vector(self) -> uint32x4_t {
unsafe {
transmute(self)
}
}
#[inline]
#[must_use]
pub(crate) fn to_signed_vector(self) -> int32x4_t {
unsafe {
transmute(self)
}
}
#[inline]
pub(crate) unsafe fn from_vector(vector: uint32x4_t) -> Self {
unsafe {
transmute(vector)
}
}
#[inline]
const fn broadcast(value: MontyField31<PMP>) -> Self {
Self([value; WIDTH])
}
#[inline(always)]
pub(crate) fn forward_butterfly(self, y: Self, roots: Self) -> (Self, Self) {
unsafe {
let x_vec = self.to_vector();
let y_vec = y.to_vector();
let sum = uint32x4_mod_add(x_vec, y_vec, PMP::PACKED_P);
let diff = aarch64::vreinterpretq_s32_u32(aarch64::vsubq_u32(x_vec, y_vec));
let roots_s = roots.to_signed_vector();
let product = mul::<PMP>(diff, roots_s);
(Self::from_vector(sum), Self::from_vector(product))
}
}
}
impl<PMP: PackedMontyParameters> From<MontyField31<PMP>> for PackedMontyField31Neon<PMP> {
#[inline]
fn from(value: MontyField31<PMP>) -> Self {
Self::broadcast(value)
}
}
impl<PMP: PackedMontyParameters> Add for PackedMontyField31Neon<PMP> {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self {
let lhs = self.to_vector();
let rhs = rhs.to_vector();
let res = uint32x4_mod_add(lhs, rhs, PMP::PACKED_P);
unsafe {
Self::from_vector(res)
}
}
}
impl<PMP: PackedMontyParameters> Sub for PackedMontyField31Neon<PMP> {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self {
let lhs = self.to_vector();
let rhs = rhs.to_vector();
let res = uint32x4_mod_sub(lhs, rhs, PMP::PACKED_P);
unsafe {
Self::from_vector(res)
}
}
}
impl<PMP: PackedMontyParameters> Neg for PackedMontyField31Neon<PMP> {
type Output = Self;
#[inline]
fn neg(self) -> Self {
let val = self.to_vector();
let res = neg::<PMP>(val);
unsafe {
Self::from_vector(res)
}
}
}
impl<PMP: PackedMontyParameters> Mul for PackedMontyField31Neon<PMP> {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self {
let lhs = self.to_signed_vector();
let rhs = rhs.to_signed_vector();
let res = mul::<PMP>(lhs, rhs);
unsafe {
Self::from_vector(res)
}
}
}
impl_add_assign!(PackedMontyField31Neon, (PackedMontyParameters, PMP));
impl_sub_assign!(PackedMontyField31Neon, (PackedMontyParameters, PMP));
impl_mul_methods!(PackedMontyField31Neon, (FieldParameters, FP));
ring_sum!(PackedMontyField31Neon, (FieldParameters, FP));
impl_rng!(PackedMontyField31Neon, (PackedMontyParameters, PMP));
impl<FP: FieldParameters> PrimeCharacteristicRing for PackedMontyField31Neon<FP> {
type PrimeSubfield = MontyField31<FP>;
const ZERO: Self = Self::broadcast(MontyField31::ZERO);
const ONE: Self = Self::broadcast(MontyField31::ONE);
const TWO: Self = Self::broadcast(MontyField31::TWO);
const NEG_ONE: Self = Self::broadcast(MontyField31::NEG_ONE);
#[inline]
fn from_prime_subfield(f: Self::PrimeSubfield) -> Self {
f.into()
}
#[inline]
fn halve(&self) -> Self {
let val = self.to_vector();
let halved = halve_neon::<FP>(val);
unsafe {
Self::from_vector(halved)
}
}
#[inline]
fn cube(&self) -> Self {
let val = self.to_signed_vector();
let res = cube::<FP>(val);
unsafe {
Self::from_vector(res)
}
}
#[inline(always)]
fn dot_product<const N: usize>(u: &[Self; N], v: &[Self; N]) -> Self {
general_dot_product::<_, _, _, N>(u, v)
}
#[inline(always)]
fn zero_vec(len: usize) -> Vec<Self> {
unsafe { reconstitute_from_base(MontyField31::<FP>::zero_vec(len * WIDTH)) }
}
#[inline(always)]
fn exp_const_u64<const POWER: u64>(&self) -> Self {
match POWER {
0 => Self::ONE,
1 => *self,
2 => self.square(),
3 => self.cube(),
4 => self.square().square(),
5 => {
let val = self.to_signed_vector();
unsafe {
let res = exp_5::<FP>(val);
Self::from_vector(res)
}
}
6 => self.square().cube(),
7 => {
let val = self.to_signed_vector();
unsafe {
let res = exp_7::<FP>(val);
Self::from_vector(res)
}
}
_ => self.exp_u64(POWER),
}
}
}
impl_add_base_field!(
PackedMontyField31Neon,
MontyField31,
(PackedMontyParameters, PMP)
);
impl_sub_base_field!(
PackedMontyField31Neon,
MontyField31,
(PackedMontyParameters, PMP)
);
impl_mul_base_field!(
PackedMontyField31Neon,
MontyField31,
(PackedMontyParameters, PMP)
);
impl_div_methods!(PackedMontyField31Neon, MontyField31, (FieldParameters, FP));
impl_sum_prod_base_field!(PackedMontyField31Neon, MontyField31, (FieldParameters, FP));
impl<FP: FieldParameters> Algebra<MontyField31<FP>> for PackedMontyField31Neon<FP> {}
impl<FP: FieldParameters + RelativelyPrimePower<D>, const D: u64> InjectiveMonomial<D>
for PackedMontyField31Neon<FP>
{
}
impl<FP: FieldParameters + RelativelyPrimePower<D>, const D: u64> PermutationMonomial<D>
for PackedMontyField31Neon<FP>
{
fn injective_exp_root_n(&self) -> Self {
FP::exp_root_d(*self)
}
}
#[inline]
#[must_use]
fn confuse_compiler(x: uint32x4_t) -> uint32x4_t {
let y;
unsafe {
asm!(
"/*{0:v}*/",
inlateout(vreg) x => y,
options(nomem, nostack, preserves_flags, pure),
);
if transmute::<uint32x4_t, [u32; 4]>(x) != transmute::<uint32x4_t, [u32; 4]>(y) {
unreachable_unchecked();
}
}
y
}
#[inline]
#[must_use]
fn mulby_mu<MPNeon: MontyParametersNeon>(val: int32x4_t) -> int32x4_t {
unsafe { aarch64::vmulq_s32(val, MPNeon::PACKED_MU) }
}
#[inline]
#[must_use]
fn get_c_hi(lhs: int32x4_t, rhs: int32x4_t) -> int32x4_t {
unsafe {
aarch64::vqdmulhq_s32(lhs, rhs)
}
}
#[inline]
#[must_use]
fn get_qp_hi<MPNeon: MontyParametersNeon>(lhs: int32x4_t, mu_rhs: int32x4_t) -> int32x4_t {
unsafe {
let q = aarch64::vmulq_s32(lhs, mu_rhs);
aarch64::vqdmulhq_s32(q, aarch64::vreinterpretq_s32_u32(MPNeon::PACKED_P))
}
}
#[inline]
#[must_use]
fn mul<MPNeon: MontyParametersNeon>(lhs: int32x4_t, rhs: int32x4_t) -> uint32x4_t {
unsafe {
let mu_rhs = mulby_mu::<MPNeon>(rhs);
let d = mul_with_precomp::<MPNeon, true>(lhs, rhs, mu_rhs);
aarch64::vreinterpretq_u32_s32(d)
}
}
#[inline]
#[must_use]
fn mul_with_precomp<MPNeon: MontyParametersNeon, const CANONICAL: bool>(
lhs: int32x4_t,
rhs: int32x4_t,
mu_rhs: int32x4_t,
) -> int32x4_t {
unsafe {
let c_hi = get_c_hi(lhs, rhs);
let qp_hi = get_qp_hi::<MPNeon>(lhs, mu_rhs);
let d = aarch64::vhsubq_s32(c_hi, qp_hi);
if CANONICAL {
let underflow = aarch64::vcltq_s32(c_hi, qp_hi);
let reduced = aarch64::vmlsq_u32(
aarch64::vreinterpretq_u32_s32(d),
confuse_compiler(underflow),
MPNeon::PACKED_P,
);
aarch64::vreinterpretq_s32_u32(reduced)
} else {
d
}
}
}
#[inline]
#[must_use]
fn cube<MPNeon: MontyParametersNeon>(val: int32x4_t) -> uint32x4_t {
unsafe {
let mu_val = mulby_mu::<MPNeon>(val);
let val_2 = mul_with_precomp::<MPNeon, false>(val, val, mu_val);
let val_3 = mul_with_precomp::<MPNeon, true>(val_2, val, mu_val);
aarch64::vreinterpretq_u32_s32(val_3)
}
}
#[inline]
#[must_use]
fn exp_5<MPNeon: MontyParametersNeon>(val: int32x4_t) -> uint32x4_t {
unsafe {
let mu_val = mulby_mu::<MPNeon>(val);
let val_2 = mul_with_precomp::<MPNeon, false>(val, val, mu_val);
let mu_val_2 = mulby_mu::<MPNeon>(val_2);
let val_3 = mul_with_precomp::<MPNeon, false>(val_2, val, mu_val);
let val_5 = mul_with_precomp::<MPNeon, true>(val_3, val_2, mu_val_2);
aarch64::vreinterpretq_u32_s32(val_5)
}
}
#[inline]
#[must_use]
fn exp_7<MPNeon: MontyParametersNeon>(val: int32x4_t) -> uint32x4_t {
unsafe {
let mu_val = mulby_mu::<MPNeon>(val);
let val_2 = mul_with_precomp::<MPNeon, false>(val, val, mu_val);
let mu_val_2 = mulby_mu::<MPNeon>(val_2);
let val_3 = mul_with_precomp::<MPNeon, false>(val_2, val, mu_val);
let mu_val_3 = mulby_mu::<MPNeon>(val_3);
let val_4 = mul_with_precomp::<MPNeon, false>(val_2, val_2, mu_val_2);
let val_7 = mul_with_precomp::<MPNeon, true>(val_4, val_3, mu_val_3);
aarch64::vreinterpretq_u32_s32(val_7)
}
}
#[inline]
#[must_use]
fn neg<MPNeon: MontyParametersNeon>(val: uint32x4_t) -> uint32x4_t {
unsafe {
let t = aarch64::vsubq_u32(MPNeon::PACKED_P, val);
let is_zero = aarch64::vceqzq_u32(val);
aarch64::vbicq_u32(t, is_zero)
}
}
impl_packed_value!(
PackedMontyField31Neon,
MontyField31,
WIDTH,
(PackedMontyParameters, PMP)
);
unsafe impl<FP: FieldParameters> PackedField for PackedMontyField31Neon<FP> {
type Scalar = MontyField31<FP>;
#[inline]
fn packed_linear_combination<const N: usize>(coeffs: &[Self::Scalar], vecs: &[Self]) -> Self {
general_dot_product::<_, _, _, N>(coeffs, vecs)
}
}
impl_packed_field_pow_2!(
PackedMontyField31Neon, (FieldParameters, FP);
[
(1, interleave_u32),
(2, interleave_u64),
],
WIDTH
);
#[inline]
unsafe fn dot_product_2<P, LHS, RHS>(lhs: &[LHS; 2], rhs: &[RHS; 2]) -> PackedMontyField31Neon<P>
where
P: FieldParameters + MontyParametersNeon,
LHS: IntoVec<P>,
RHS: IntoVec<P>,
{
unsafe {
let mut sum_l = aarch64::vmull_u32(
aarch64::vget_low_u32(lhs[0].into_vec()),
aarch64::vget_low_u32(rhs[0].into_vec()),
);
sum_l = aarch64::vmlal_u32(
sum_l,
aarch64::vget_low_u32(lhs[1].into_vec()),
aarch64::vget_low_u32(rhs[1].into_vec()),
);
let mut sum_h = aarch64::vmull_high_u32(lhs[0].into_vec(), rhs[0].into_vec());
sum_h = aarch64::vmlal_high_u32(sum_h, lhs[1].into_vec(), rhs[1].into_vec());
let c_lo = aarch64::vuzp1q_u32(
aarch64::vreinterpretq_u32_u64(sum_l),
aarch64::vreinterpretq_u32_u64(sum_h),
);
let q = aarch64::vmulq_u32(c_lo, aarch64::vreinterpretq_u32_s32(P::PACKED_MU));
let d_l = aarch64::vmlsl_u32(
sum_l,
aarch64::vget_low_u32(q),
aarch64::vget_low_u32(P::PACKED_P),
);
let d_h = aarch64::vmlsl_high_u32(sum_h, q, P::PACKED_P);
let d = aarch64::vuzp2q_u32(
aarch64::vreinterpretq_u32_u64(d_l),
aarch64::vreinterpretq_u32_u64(d_h),
);
let underflow = aarch64::vcgeq_u32(d, aarch64::vdupq_n_u32(1u32 << 31));
let canonical_res = aarch64::vmlsq_u32(d, underflow, P::PACKED_P);
PackedMontyField31Neon::from_vector(canonical_res)
}
}
#[inline(always)]
fn general_dot_product<P, LHS, RHS, const N: usize>(
lhs: &[LHS],
rhs: &[RHS],
) -> PackedMontyField31Neon<P>
where
P: FieldParameters + MontyParametersNeon,
LHS: IntoVec<P> + Into<PackedMontyField31Neon<P>>,
RHS: IntoVec<P> + Into<PackedMontyField31Neon<P>>,
{
assert_eq!(lhs.len(), N);
assert_eq!(rhs.len(), N);
match N {
0 => PackedMontyField31Neon::<P>::ZERO,
1 => lhs[0].into() * rhs[0].into(),
2 => unsafe { dot_product_2(&[lhs[0], lhs[1]], &[rhs[0], rhs[1]]) },
3 => {
let lhs_packed = [
lhs[0].into(),
lhs[1].into(),
lhs[2].into(),
PackedMontyField31Neon::<P>::ZERO,
];
let rhs_packed = [
rhs[0].into(),
rhs[1].into(),
rhs[2].into(),
PackedMontyField31Neon::<P>::ZERO,
];
unsafe { dot_product_4(&lhs_packed, &rhs_packed) }
}
4 => unsafe {
dot_product_4(
&[lhs[0], lhs[1], lhs[2], lhs[3]],
&[rhs[0], rhs[1], rhs[2], rhs[3]],
)
},
64 => {
let sum_4s: [PackedMontyField31Neon<P>; 16] = core::array::from_fn(|i| {
let start = i * 4;
unsafe {
dot_product_4(
&[lhs[start], lhs[start + 1], lhs[start + 2], lhs[start + 3]],
&[rhs[start], rhs[start + 1], rhs[start + 2], rhs[start + 3]],
)
}
});
PackedMontyField31Neon::<P>::sum_array::<16>(&sum_4s)
}
_ => {
let mut acc = unsafe {
dot_product_4(
&[lhs[0], lhs[1], lhs[2], lhs[3]],
&[rhs[0], rhs[1], rhs[2], rhs[3]],
)
};
for i in (4..N).step_by(4) {
if i + 3 < N {
acc += unsafe {
dot_product_4(
&[lhs[i], lhs[i + 1], lhs[i + 2], lhs[i + 3]],
&[rhs[i], rhs[i + 1], rhs[i + 2], rhs[i + 3]],
)
};
}
}
match N % 4 {
0 => acc,
1 => {
let rem_start = N - 1;
let lhs_rem: [_; 1] = core::array::from_fn(|i| lhs[rem_start + i]);
let rhs_rem: [_; 1] = core::array::from_fn(|i| rhs[rem_start + i]);
acc + general_dot_product::<_, _, _, 1>(&lhs_rem, &rhs_rem)
}
2 => {
let rem_start = N - 2;
let lhs_rem: [_; 2] = core::array::from_fn(|i| lhs[rem_start + i]);
let rhs_rem: [_; 2] = core::array::from_fn(|i| rhs[rem_start + i]);
acc + general_dot_product::<_, _, _, 2>(&lhs_rem, &rhs_rem)
}
3 => {
let rem_start = N - 3;
let lhs_rem: [_; 3] = core::array::from_fn(|i| lhs[rem_start + i]);
let rhs_rem: [_; 3] = core::array::from_fn(|i| rhs[rem_start + i]);
acc + general_dot_product::<_, _, _, 3>(&lhs_rem, &rhs_rem)
}
_ => unreachable!(),
}
}
}
}
#[inline]
unsafe fn dot_product_4<P, LHS, RHS>(lhs: &[LHS; 4], rhs: &[RHS; 4]) -> PackedMontyField31Neon<P>
where
P: FieldParameters + MontyParametersNeon,
LHS: IntoVec<P>,
RHS: IntoVec<P>,
{
unsafe {
let mut sum_l = aarch64::vmull_u32(
aarch64::vget_low_u32(lhs[0].into_vec()),
aarch64::vget_low_u32(rhs[0].into_vec()),
);
sum_l = aarch64::vmlal_u32(
sum_l,
aarch64::vget_low_u32(lhs[1].into_vec()),
aarch64::vget_low_u32(rhs[1].into_vec()),
);
sum_l = aarch64::vmlal_u32(
sum_l,
aarch64::vget_low_u32(lhs[2].into_vec()),
aarch64::vget_low_u32(rhs[2].into_vec()),
);
sum_l = aarch64::vmlal_u32(
sum_l,
aarch64::vget_low_u32(lhs[3].into_vec()),
aarch64::vget_low_u32(rhs[3].into_vec()),
);
let mut sum_h = aarch64::vmull_high_u32(lhs[0].into_vec(), rhs[0].into_vec());
sum_h = aarch64::vmlal_high_u32(sum_h, lhs[1].into_vec(), rhs[1].into_vec());
sum_h = aarch64::vmlal_high_u32(sum_h, lhs[2].into_vec(), rhs[2].into_vec());
sum_h = aarch64::vmlal_high_u32(sum_h, lhs[3].into_vec(), rhs[3].into_vec());
let c_lo = aarch64::vuzp1q_u32(
aarch64::vreinterpretq_u32_u64(sum_l),
aarch64::vreinterpretq_u32_u64(sum_h),
);
let c_hi = aarch64::vuzp2q_u32(
aarch64::vreinterpretq_u32_u64(sum_l),
aarch64::vreinterpretq_u32_u64(sum_h),
);
let c_hi_sub = aarch64::vsubq_u32(c_hi, P::PACKED_P);
let c_hi_prime = aarch64::vminq_u32(c_hi, c_hi_sub);
let q = aarch64::vmulq_u32(c_lo, aarch64::vreinterpretq_u32_s32(P::PACKED_MU));
let qp_l = aarch64::vmull_u32(aarch64::vget_low_u32(q), aarch64::vget_low_u32(P::PACKED_P));
let qp_h = aarch64::vmull_high_u32(q, P::PACKED_P);
let qp_hi = aarch64::vuzp2q_u32(
aarch64::vreinterpretq_u32_u64(qp_l),
aarch64::vreinterpretq_u32_u64(qp_h),
);
let d = aarch64::vsubq_u32(c_hi_prime, qp_hi);
let underflow = aarch64::vcltq_u32(c_hi_prime, qp_hi);
let canonical_res = aarch64::vmlsq_u32(d, underflow, P::PACKED_P);
PackedMontyField31Neon::from_vector(canonical_res)
}
}
#[inline]
pub(crate) fn quartic_mul_packed<FP, const WIDTH: usize>(
a: &[MontyField31<FP>; WIDTH],
b: &[MontyField31<FP>; WIDTH],
res: &mut [MontyField31<FP>; WIDTH],
) where
FP: FieldParameters + BinomialExtensionData<WIDTH> + MontyParametersNeon,
{
assert_eq!(WIDTH, 4);
let packed_b = PackedMontyField31Neon([b[0], b[1], b[2], b[3]]);
let w_b = FP::mul_w(packed_b).0;
let cols = [
PackedMontyField31Neon([b[0], b[1], b[2], b[3]]),
PackedMontyField31Neon([w_b[3], b[0], b[1], b[2]]),
PackedMontyField31Neon([w_b[2], w_b[3], b[0], b[1]]),
PackedMontyField31Neon([w_b[1], w_b[2], w_b[3], b[0]]),
];
let a_coeffs = [a[0], a[1], a[2], a[3]];
let result = unsafe { dot_product_4(&a_coeffs, &cols) };
res.copy_from_slice(&result.0);
}
#[inline]
pub(crate) fn quintic_mul_packed<FP, const WIDTH: usize>(
a: &[MontyField31<FP>; WIDTH],
b: &[MontyField31<FP>; WIDTH],
res: &mut [MontyField31<FP>; WIDTH],
) where
FP: FieldParameters + BinomialExtensionData<WIDTH>,
{
assert_eq!(WIDTH, 5);
let packed_b = PackedMontyField31Neon([b[1], b[2], b[3], b[4]]);
let w_b = FP::mul_w(packed_b).0;
let w_b1 = w_b[0];
let w_b2 = w_b[1];
let w_b3 = w_b[2];
let w_b4 = w_b[3];
let lhs: [PackedMontyField31Neon<FP>; 5] = [
a[0].into(),
a[1].into(),
a[2].into(),
a[3].into(),
a[4].into(),
];
let rhs = [
PackedMontyField31Neon([b[0], b[1], b[2], b[3]]),
PackedMontyField31Neon([w_b4, b[0], b[1], b[2]]),
PackedMontyField31Neon([w_b3, w_b4, b[0], b[1]]),
PackedMontyField31Neon([w_b2, w_b3, w_b4, b[0]]),
PackedMontyField31Neon([w_b1, w_b2, w_b3, w_b4]),
];
let dot = PackedMontyField31Neon::dot_product(&lhs, &rhs).0;
res[..4].copy_from_slice(&dot);
res[4] =
MontyField31::dot_product::<5>(a[..].try_into().unwrap(), &[b[4], b[3], b[2], b[1], b[0]]);
}
#[inline]
pub(crate) fn quintic_mul_packed_trinomial<FP: FieldParameters>(
a: &[MontyField31<FP>; 5],
b: &[MontyField31<FP>; 5],
res: &mut [MontyField31<FP>; 5],
) {
let b0_minus_b3 = b[0] - b[3];
let b1_minus_b4 = b[1] - b[4];
let b4_minus_b2 = b[4] - b[2];
let b3_plus_b4_minus_b_1 = b[3] - b1_minus_b4;
let lhs: [PackedMontyField31Neon<FP>; 5] = [
a[0].into(),
a[1].into(),
a[2].into(),
a[3].into(),
a[4].into(),
];
let rhs = [
PackedMontyField31Neon([b[0], b[1], b[2], b[3]]),
PackedMontyField31Neon([b[4], b[0], b1_minus_b4, b[2]]),
PackedMontyField31Neon([b[3], b[4], b0_minus_b3, b1_minus_b4]),
PackedMontyField31Neon([b[2], b[3], b4_minus_b2, b0_minus_b3]),
PackedMontyField31Neon([b1_minus_b4, b[2], b3_plus_b4_minus_b_1, b4_minus_b2]),
];
let dot = PackedMontyField31Neon::dot_product(&lhs, &rhs).0;
res[..4].copy_from_slice(&dot);
res[4] = MontyField31::dot_product::<5>(
&[a[0], a[1], a[2], a[3], a[4]],
&[b[4], b[3], b[2], b1_minus_b4, b0_minus_b3],
);
}
#[inline]
pub(crate) fn octic_mul_packed<FP, const WIDTH: usize>(
a: &[MontyField31<FP>; WIDTH],
b: &[MontyField31<FP>; WIDTH],
res: &mut [MontyField31<FP>; WIDTH],
) where
FP: FieldParameters + BinomialExtensionData<WIDTH>,
{
assert_eq!(WIDTH, 8);
let packed_b_lo = PackedMontyField31Neon([b[0], b[1], b[2], b[3]]);
let packed_b_hi = PackedMontyField31Neon([b[4], b[5], b[6], b[7]]);
let w_b_lo = FP::mul_w(packed_b_lo).0;
let w_b_hi = FP::mul_w(packed_b_hi).0;
let lhs: [PackedMontyField31Neon<FP>; 8] = [
a[0].into(),
a[1].into(),
a[2].into(),
a[3].into(),
a[4].into(),
a[5].into(),
a[6].into(),
a[7].into(),
];
let rhs_0 = [
PackedMontyField31Neon([b[0], b[1], b[2], b[3]]),
PackedMontyField31Neon([w_b_hi[3], b[0], b[1], b[2]]),
PackedMontyField31Neon([w_b_hi[2], w_b_hi[3], b[0], b[1]]),
PackedMontyField31Neon([w_b_hi[1], w_b_hi[2], w_b_hi[3], b[0]]),
PackedMontyField31Neon([w_b_hi[0], w_b_hi[1], w_b_hi[2], w_b_hi[3]]),
PackedMontyField31Neon([w_b_lo[3], w_b_hi[0], w_b_hi[1], w_b_hi[2]]),
PackedMontyField31Neon([w_b_lo[2], w_b_lo[3], w_b_hi[0], w_b_hi[1]]),
PackedMontyField31Neon([w_b_lo[1], w_b_lo[2], w_b_lo[3], w_b_hi[0]]),
];
let rhs_1 = [
PackedMontyField31Neon([b[4], b[5], b[6], b[7]]),
PackedMontyField31Neon([b[3], b[4], b[5], b[6]]),
PackedMontyField31Neon([b[2], b[3], b[4], b[5]]),
PackedMontyField31Neon([b[1], b[2], b[3], b[4]]),
PackedMontyField31Neon([b[0], b[1], b[2], b[3]]),
PackedMontyField31Neon([w_b_hi[3], b[0], b[1], b[2]]),
PackedMontyField31Neon([w_b_hi[2], w_b_hi[3], b[0], b[1]]),
PackedMontyField31Neon([w_b_hi[1], w_b_hi[2], w_b_hi[3], b[0]]),
];
let dot_0 = PackedMontyField31Neon::dot_product(&lhs, &rhs_0).0;
let dot_1 = PackedMontyField31Neon::dot_product(&lhs, &rhs_1).0;
res[..4].copy_from_slice(&dot_0);
res[4..].copy_from_slice(&dot_1);
}
#[inline]
pub(crate) fn base_mul_packed<FP, const WIDTH: usize>(
a: [MontyField31<FP>; WIDTH],
b: MontyField31<FP>,
res: &mut [MontyField31<FP>; WIDTH],
) where
FP: FieldParameters + BinomialExtensionData<WIDTH>,
{
match WIDTH {
1 => res[0] = a[0] * b,
4 => {
let lhs = PackedMontyField31Neon([a[0], a[1], a[2], a[3]]);
let out = lhs * b;
res.copy_from_slice(&out.0[..4]);
}
5 => {
let lhs = PackedMontyField31Neon([a[0], a[1], a[2], a[3]]);
let out = lhs * b;
res[4] = a[4] * b;
res[..4].copy_from_slice(&out.0[..4]);
}
8 => {
let lhs_lo = PackedMontyField31Neon([a[0], a[1], a[2], a[3]]);
let lhs_hi = PackedMontyField31Neon([a[4], a[5], a[6], a[7]]);
let out_lo = lhs_lo * b;
let out_hi = lhs_hi * b;
res[..4].copy_from_slice(&out_lo.0);
res[4..].copy_from_slice(&out_hi.0);
}
_ => panic!("Unsupported binomial extension degree: {}", WIDTH),
}
}
#[inline(always)]
#[must_use]
pub(crate) fn exp_small<PMP, const D: u64>(val: int32x4_t) -> uint32x4_t
where
PMP: PackedMontyParameters + FieldParameters,
{
match D {
3 => cube::<PMP>(val),
5 => exp_5::<PMP>(val),
7 => exp_7::<PMP>(val),
_ => panic!("No exp function for given D"),
}
}