use crate::int::algos::sum_sq::sum_sq_schoolbook::sig_len;
use crate::int::algos::support::limbs::add_assign;
use crate::int::types::compute_limbs::{ComputeLimbs, Limbs};
use crate::int::types::Int;
#[inline]
fn sqr_full(x: &[u64], l: usize, out: &mut [u64]) {
let mut acc: u128 = 0;
let mut hi: u64 = 0;
let ncol = 2 * l; let mut col = 0;
while col < ncol {
let lo_i = if col >= l { col - (l - 1) } else { 0 };
let mut i = lo_i;
while 2 * i <= col {
let j = col - i;
let p = (x[i] as u128) * (x[j] as u128);
let reps = if i == j { 1 } else { 2 };
let mut r = 0;
while r < reps {
let (s, c) = acc.overflowing_add(p);
acc = s;
hi += c as u64;
r += 1;
}
i += 1;
}
out[col] = acc as u64;
acc = (acc >> 64) + ((hi as u128) << 64);
hi = 0;
col += 1;
}
}
#[inline]
pub(crate) fn sum_sq_radicand_comba<const N: usize>(ma: &[u64], mb: &[u64], out: &mut [u64]) -> usize
where
Limbs<N>: ComputeLimbs,
{
let la = sig_len(ma);
let lb = sig_len(mb);
sqr_full(ma, la, &mut out[..2 * la]);
let mut bsq_buf = Limbs::<N>::double_buffered_u64();
let bsq = bsq_buf.as_mut();
sqr_full(mb, lb, &mut bsq[..2 * lb]);
let span = (2 * la).max(2 * lb) + 1;
add_assign(&mut out[..span], &bsq[..2 * lb]);
sig_len(&out[..span])
}
#[inline]
#[must_use]
pub(crate) fn sum_sq_comba<const N: usize>(a: Int<N>, b: Int<N>) -> Option<Int<N>>
where
Limbs<N>: ComputeLimbs,
{
let ma = a.unsigned_abs();
let mb = b.unsigned_abs();
let mut n_buf = Limbs::<N>::double_buffered_u64();
let n = n_buf.as_mut();
let nl = sum_sq_radicand_comba::<N>(ma.as_limbs(), mb.as_limbs(), n);
if nl > N || (nl == N && (n[N - 1] >> 63) != 0) {
return None;
}
let mut out = [0u64; N];
out.copy_from_slice(&n[..N]);
Some(Int::<N>::from_limbs(out))
}
#[cfg(test)]
mod tests {
use super::sum_sq_comba;
use crate::int::algos::sum_sq::sum_sq_schoolbook::sum_sq_schoolbook;
use crate::int::types::Int;
fn mix(s: &mut u64) -> u64 {
*s = s.wrapping_add(0x9E37_79B9_7F4A_7C15);
let mut z = *s;
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
z ^ (z >> 31)
}
fn rand_int<const N: usize>(s: &mut u64) -> Int<N> {
let mut limbs = [0u64; N];
for limb in limbs.iter_mut() {
*limb = mix(s);
}
limbs[N - 1] &= i64::MAX as u64;
Int::<N>::from_limbs(limbs)
}
fn diff_at<const N: usize>()
where
crate::int::types::compute_limbs::Limbs<N>: crate::int::types::compute_limbs::ComputeLimbs,
{
let mut s = 0x0123_4567_89AB_CDEF_u64 ^ (N as u64);
for _ in 0..400 {
let mut a = rand_int::<N>(&mut s);
let mut b = rand_int::<N>(&mut s);
if mix(&mut s) & 1 == 0 {
a = Int::<N>::from_limbs({
let mut l = *a.as_limbs();
l[1..N].fill(0);
l[0] &= 0xFFFF_FFFF; l
});
}
if mix(&mut s) & 1 == 0 {
b = Int::<N>::ZERO;
}
assert_eq!(
sum_sq_comba::<N>(a, b),
sum_sq_schoolbook::<N>(a, b),
"N={N} a={:?} b={:?}",
a.as_limbs(),
b.as_limbs()
);
}
let three = Int::<N>::from_i64(3);
let four = Int::<N>::from_i64(4);
assert_eq!(sum_sq_comba::<N>(three, four), sum_sq_schoolbook::<N>(three, four));
let z = Int::<N>::ZERO;
assert_eq!(sum_sq_comba::<N>(z, z), sum_sq_schoolbook::<N>(z, z));
assert_eq!(sum_sq_comba::<N>(Int::<N>::MAX, Int::<N>::MAX), sum_sq_schoolbook::<N>(Int::<N>::MAX, Int::<N>::MAX));
}
#[test]
fn sum_sq_comba_matches_schoolbook() {
diff_at::<2>();
diff_at::<3>();
diff_at::<4>();
diff_at::<6>();
diff_at::<8>();
}
}