use super::basis::{TAIL_128, poly_mul};
const ROOT_X: u128 = 0x2492_4924_9249_2492_6db6_db6d_b6db_6da4;
const _: () = assert!(poly_mul(ROOT_X, ROOT_X, 128, TAIL_128) == 2);
#[inline]
pub(super) const fn compact_even(mut x: u64) -> u64 {
x &= 0x5555_5555_5555_5555;
x = (x | (x >> 1)) & 0x3333_3333_3333_3333;
x = (x | (x >> 2)) & 0x0f0f_0f0f_0f0f_0f0f;
x = (x | (x >> 4)) & 0x00ff_00ff_00ff_00ff;
x = (x | (x >> 8)) & 0x0000_ffff_0000_ffff;
(x | (x >> 16)) & 0xffff_ffff
}
#[inline]
const fn unshuffle(a: u128) -> (u64, u64) {
let (lo, hi) = (a as u64, (a >> 64) as u64);
(
compact_even(lo) | (compact_even(hi) << 32),
compact_even(lo >> 1) | (compact_even(hi >> 1) << 32),
)
}
#[inline]
pub(crate) fn poly_sqrt_128(a: u128) -> u128 {
let (even, odd) = unshuffle(a);
u128::from(even) ^ super::poly_mul_128_by_64(ROOT_X, odd)
}
#[cfg(test)]
mod tests {
use proptest::prelude::*;
use super::*;
proptest! {
#[test]
fn unshuffle_matches_individual_coefficients(a: u128) {
let (even, odd) = unshuffle(a);
for i in 0..64 {
prop_assert_eq!((even >> i) & 1, ((a >> (2 * i)) & 1) as u64);
prop_assert_eq!((odd >> i) & 1, ((a >> (2 * i + 1)) & 1) as u64);
}
}
}
#[test]
fn every_basis_vector_squares_back() {
for i in 0..128 {
let a = 1u128 << i;
let root = poly_sqrt_128(a);
assert_eq!(poly_mul(root, root, 128, TAIL_128), a);
}
assert_eq!(poly_sqrt_128(0), 0);
let root = poly_sqrt_128(u128::MAX);
assert_eq!(poly_mul(root, root, 128, TAIL_128), u128::MAX);
}
}