use crate::bounded::NonNegative;
use crate::error::{Error, Result};
use crate::traits::CordicNumber;
#[must_use = "returns the square root result which should be handled"]
#[cfg_attr(feature = "verify-no-panic", no_panic::no_panic)]
pub fn sqrt<T: CordicNumber>(x: T) -> Result<T> {
NonNegative::new(x)
.map(sqrt_nonneg)
.ok_or_else(|| Error::domain("sqrt", "non-negative value"))
}
#[must_use]
#[cfg_attr(feature = "verify-no-panic", no_panic::no_panic)]
pub fn sqrt_nonneg<T: CordicNumber>(x: NonNegative<T>) -> T {
let x = x.get();
let zero = T::zero();
let one = T::one();
let half = T::half();
if x == zero {
return zero;
}
if x == one {
return one;
}
let mut guess = if x > one {
let mut g = one;
let four = T::two().saturating_mul(T::two());
let mut test = x;
let mut iter_guard = 0u32;
while test >= four && iter_guard < 64 {
test = test >> 2;
g = g << 1;
iter_guard += 1;
}
let quotient = x.div(g);
g.saturating_add(quotient) >> 1
} else {
x
};
let iterations = (T::frac_bits() / 2).clamp(8, 20);
#[allow(
clippy::cast_possible_wrap,
clippy::cast_sign_loss,
reason = "frac_bits bounded by type size"
)]
let epsilon_shift = (63i32 - (T::frac_bits() / 2) as i32).max(0) as u32;
let epsilon = T::from_i1f63(1i64 << epsilon_shift);
for _ in 0..iterations.saturating_sub(1) {
let quotient = x.div(guess);
let sum = guess.saturating_add(quotient);
let new_guess = sum.saturating_mul(half);
let diff = if new_guess > guess {
new_guess.saturating_sub(guess)
} else {
guess.saturating_sub(new_guess)
};
if diff <= epsilon {
return new_guess;
}
guess = new_guess;
}
let quotient = x.div(guess);
let sum = guess.saturating_add(quotient);
sum.saturating_mul(half)
}