use crate::int::types::Uint;
#[inline]
pub(crate) const fn cube_fused_comba<const N: usize>(x: Uint<N>) -> Uint<N> {
let limbs = x.as_limbs();
let mut out = [0u64; N];
let mut acc_lo: u128 = 0;
let mut acc_hi: u128 = 0;
let mut col = 0;
while col < N {
let mut i = 0;
while 3 * i <= col {
let xi = limbs[i] as u128;
let mut j = i;
while i + 2 * j <= col {
let k = col - i - j;
let xj = limbs[j] as u128;
let xk = limbs[k] as u128;
let ab = xi * xj; let ab_lo = ab as u64 as u128;
let ab_hi = ab >> 64;
let t0 = ab_lo * xk; let t1 = ab_hi * xk; let t1_lo = (t1 as u64 as u128) << 64; let t1_hi = t1 >> 64; let (p_lo, c0) = t0.overflowing_add(t1_lo);
let p_hi: u128 = t1_hi + (c0 as u128);
let reps = if i == j && j == k {
1
} else if i == j || j == k {
3
} else {
6
};
let mut r = 0;
while r < reps {
let (s_lo, cc) = acc_lo.overflowing_add(p_lo);
acc_lo = s_lo;
acc_hi = acc_hi.wrapping_add(p_hi).wrapping_add(cc as u128);
r += 1;
}
j += 1;
}
i += 1;
}
out[col] = acc_lo as u64;
acc_lo = (acc_lo >> 64) | ((acc_hi & ((1u128 << 64) - 1)) << 64);
acc_hi >>= 64;
col += 1;
}
Uint::<N>::from_limbs(out)
}
#[cfg(test)]
mod tests {
use super::cube_fused_comba;
use crate::int::algos::cube::cube_schoolbook::cube_schoolbook;
use crate::int::types::Uint;
fn diff_at<const N: usize>(seeds: &[u64]) {
for &seed in seeds {
let mut limbs = [0u64; N];
let mut s = seed;
for limb in limbs.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 x = Uint::<N>::from_limbs(limbs);
let got = cube_fused_comba::<N>(x);
let want = cube_schoolbook::<N>(x);
assert_eq!(got.as_limbs(), want.as_limbs(), "N={N} seed={seed:#x}");
}
}
fn all_ones_at<const N: usize>() {
let x = Uint::<N>::from_limbs([u64::MAX; N]);
let got = cube_fused_comba::<N>(x);
let want = cube_schoolbook::<N>(x);
assert_eq!(got.as_limbs(), want.as_limbs(), "all-ones N={N}");
}
#[test]
fn cube_fused_matches_schoolbook_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::<3>();
all_ones_at::<4>();
all_ones_at::<8>();
all_ones_at::<16>();
}
#[test]
fn cube_small_exact_values() {
let got = cube_fused_comba::<2>(Uint::<2>::from_limbs([3, 0]));
assert_eq!(got.as_limbs(), &[27, 0]);
let got = cube_fused_comba::<1>(Uint::<1>::from_limbs([2]));
assert_eq!(got.as_limbs(), &[8]);
}
}