#[inline]
pub(crate) const fn sqr_low_fixed<const N: usize>(x: &[u64; N], out: &mut [u64; N]) {
let mut acc: u128 = 0;
let mut hi: u64 = 0;
let mut col = 0;
while col < N {
let mut i = 0;
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;
}
}
#[cfg(test)]
mod tests {
use super::sqr_low_fixed;
use crate::int::algos::mul::mul_schoolbook::mul_low_fixed;
fn diff_at<const N: usize>(seeds: &[u64]) {
for &seed in seeds {
let mut x = [0u64; N];
let mut s = seed;
for limb in x.iter_mut() {
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);
*limb = z ^ (z >> 31);
}
let mut got = [0u64; N];
sqr_low_fixed::<N>(&x, &mut got);
let mut want = [0u64; N];
mul_low_fixed::<N>(&x, &x, &mut want);
assert_eq!(got, want, "N={N} seed={seed:#x} x={x:?}");
}
}
fn all_ones_at<const N: usize>() {
let x = [u64::MAX; N];
let mut got = [0u64; N];
sqr_low_fixed::<N>(&x, &mut got);
let mut want = [0u64; N];
mul_low_fixed::<N>(&x, &x, &mut want);
assert_eq!(got, want, "all-ones N={N}");
}
#[test]
fn sqr_matches_mul_low_fixed_all_widths() {
let seeds: [u64; 8] = [0, 1, 2, 3, 0xDEAD_BEEF, 0xFFFF_FFFF_FFFF_FFFF, 7, 0x1357_9BDF];
diff_at::<1>(&seeds);
diff_at::<2>(&seeds);
diff_at::<3>(&seeds);
diff_at::<4>(&seeds);
diff_at::<6>(&seeds);
diff_at::<8>(&seeds);
diff_at::<16>(&seeds);
all_ones_at::<1>();
all_ones_at::<2>();
all_ones_at::<4>();
all_ones_at::<8>();
all_ones_at::<16>();
}
#[test]
fn sqr_small_exact_values() {
let mut out = [0u64; 2];
sqr_low_fixed::<2>(&[3, 0], &mut out);
assert_eq!(out, [9, 0]); sqr_low_fixed::<2>(&[0, 1], &mut out);
assert_eq!(out, [0, 0]); sqr_low_fixed::<2>(&[u64::MAX, 0], &mut out);
assert_eq!(out, [1, 0xFFFF_FFFF_FFFF_FFFE]);
}
}