use crate::{
add,
arch::ntt::{Lane, CRT_INV_IJ, MODULI, OMEGA_MAX, P0, P1, P2},
arch::word::{SignedWord, Word},
memory::Memory,
mul::ntt::{self, bit_len, coeff_count, do_crt, pack, transform, NttGeometry},
Sign::{self, *},
};
#[must_use]
#[inline]
pub(crate) fn add_signed_sqr_same_len(
c: &mut [Word],
sign: Sign,
a: &[Word],
memory: &mut Memory,
) -> SignedWord {
let n = a.len();
debug_assert!(c.len() == 2 * n);
add_signed_sqr_conv(c, sign, a, memory)
}
fn add_signed_sqr_conv(c: &mut [Word], sign: Sign, a: &[Word], memory: &mut Memory) -> SignedWord {
let la = a.len();
debug_assert!(la > 0);
let (b_pack, nn, k_eff) = ntt::select_params(la, la);
let la_bits = bit_len(a);
debug_assert!(la_bits > 0);
let coeffs_a = coeff_count(la_bits, b_pack);
let output_coeffs = 2 * coeffs_a - 1;
let out_words = 2 * la;
let geom = NttGeometry {
nn,
b_pack,
k_eff,
output_coeffs,
};
let (prod, mut m) = memory.allocate_slice_fill::<Word>(out_words, 0);
let (residues, mut m) = m.allocate_slice_fill::<Lane>(k_eff * nn, 0);
let (a_lane, mut m) = m.allocate_slice_fill::<Lane>(nn, 0);
let (fwd_twiddles, mut m) = m.allocate_slice_fill::<Lane>(nn / 2, 0);
let (inv_twiddles, _) = m.allocate_slice_fill::<Lane>(nn / 2, 0);
for (pi, &omega) in OMEGA_MAX.iter().enumerate().take(k_eff) {
match pi {
0 => {
transform::precompute_twiddles(fwd_twiddles, nn, omega, false, &P0);
transform::precompute_twiddles(inv_twiddles, nn, omega, true, &P0);
process_prime_square(
a,
a_lane,
fwd_twiddles,
inv_twiddles,
residues,
pi,
&geom,
&P0,
);
}
1 => {
transform::precompute_twiddles(fwd_twiddles, nn, omega, false, &P1);
transform::precompute_twiddles(inv_twiddles, nn, omega, true, &P1);
process_prime_square(
a,
a_lane,
fwd_twiddles,
inv_twiddles,
residues,
pi,
&geom,
&P1,
);
}
2 => {
transform::precompute_twiddles(fwd_twiddles, nn, omega, false, &P2);
transform::precompute_twiddles(inv_twiddles, nn, omega, true, &P2);
process_prime_square(
a,
a_lane,
fwd_twiddles,
inv_twiddles,
residues,
pi,
&geom,
&P2,
);
}
_ => unreachable!(),
}
}
do_crt::<crate::arch::word::TripleWord>(prod, residues, &geom, &MODULI, &CRT_INV_IJ);
match sign {
Positive => add::add_signed_in_place(&mut c[..out_words], Positive, &prod[..out_words]),
Negative => add::add_signed_in_place(&mut c[..out_words], Negative, &prod[..out_words]),
}
}
#[allow(clippy::too_many_arguments)]
fn process_prime_square<R: num_modular::Reducer<Lane>>(
a: &[Word],
a_lane: &mut [Lane],
fwd_twiddles: &[Lane],
inv_twiddles: &[Lane],
residues: &mut [Lane],
pi: usize,
geom: &NttGeometry,
r: &R,
) {
let nn = geom.nn;
pack::pack(a_lane, a, geom.b_pack, nn);
for c in a_lane[..nn].iter_mut() {
*c = r.transform(*c);
}
transform::bit_reverse(&mut a_lane[..nn]);
transform::forward(&mut a_lane[..nn], fwd_twiddles, r);
for c in a_lane[..nn].iter_mut() {
*c = r.sqr(*c);
}
transform::inverse(&mut a_lane[..nn], inv_twiddles, r);
for c in a_lane[..nn].iter_mut() {
*c = r.residue(*c);
}
let offset = pi * nn;
residues[offset..offset + nn].copy_from_slice(&a_lane[..nn]);
}