#[must_use]
pub const fn isqrt(v: u32) -> u32 {
if v < 2 {
return v;
}
let bits = 32 - v.leading_zeros();
let mut x = 1u32 << bits.div_ceil(2);
let mut y = (x + v / x) / 2;
while y < x {
x = y;
y = (x + v / x) / 2;
}
x
}
#[must_use]
pub const fn isqrt_restoring(v: u32) -> u32 {
let mut rem: u32 = 0;
let mut root: u32 = 0;
let mut i: u32 = 16;
while i > 0 {
i -= 1;
root <<= 1;
rem = (rem << 2) | ((v >> (i * 2)) & 3);
if rem > root {
rem -= root + 1;
root += 2;
}
}
root >> 1
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn isqrt_is_floor_sqrt() {
for v in 0..70_000u32 {
let r = isqrt(v);
assert!(r * r <= v && (r + 1) * (r + 1) > v, "v={v} r={r}");
}
assert_eq!(isqrt(u32::MAX), 65_535);
for v in 0u32..200_000 {
assert_eq!(isqrt(v), (f64::from(v)).sqrt().floor() as u32, "v={v}");
}
for b in 0..32u32 {
for d in 0..3u32 {
for v in [(1u32 << b).saturating_add(d), (1u32 << b).saturating_sub(d)] {
assert_eq!(isqrt(v), (f64::from(v)).sqrt().floor() as u32, "v={v}");
}
}
}
for r in [256u32, 1_000, 4_096, 65_535] {
assert_eq!(isqrt(r * r), r);
assert_eq!(isqrt(r * r - 1), r - 1);
}
}
}