use crate::soft_f32::F32;
use core::cmp::Ordering;
pub(crate) const fn sqrtf(x: F32) -> F32 {
const TINY: F32 = f32!(1.0e-30);
let mut z: F32;
let sign: i32 = 0x80000000_u32 as i32;
let mut ix: i32;
let mut s: i32;
let mut q: i32;
let mut m: i32;
let mut t: i32;
let mut i: i32;
let mut r: u32;
ix = x.to_bits() as i32;
if (ix as u32 & 0x7f800000) == 0x7f800000 {
return x.mul(x).add(x);
}
if ix <= 0 {
if (ix & !sign) == 0 {
return x;
}
if ix < 0 {
return (x.sub(x)).div(x.sub(x));
}
}
m = ix >> 23;
if m == 0 {
i = 0;
while ix & 0x00800000 == 0 {
ix <<= 1;
i = i + 1;
}
m -= i - 1;
}
m -= 127;
ix = (ix & 0x007fffff) | 0x00800000;
if m & 1 == 1 {
ix += ix;
}
m >>= 1;
ix += ix;
q = 0;
s = 0;
r = 0x01000000;
while r != 0 {
t = s + r as i32;
if t <= ix {
s = t + r as i32;
ix -= t;
q += r as i32;
}
ix += ix;
r >>= 1;
}
if ix != 0 {
z = f32!(1.0).sub(TINY);
if ge(z, f32!(1.0)) {
z = f32!(1.0).add(TINY);
if gt(z, f32!(1.0)) {
q += 2;
} else {
q += q & 1;
}
}
}
ix = (q >> 1) + 0x3f000000;
ix += m << 23;
F32::from_bits(ix as u32)
}
const fn gt(l: F32, r: F32) -> bool {
if let Some(ord) = l.cmp(r) {
match ord {
Ordering::Greater => true,
_ => false,
}
} else {
panic!("Failed to compare values");
}
}
const fn ge(l: F32, r: F32) -> bool {
if let Some(ord) = l.cmp(r) {
match ord {
Ordering::Less => false,
_ => true,
}
} else {
panic!("Failed to compare values");
}
}
#[cfg(test)]
mod tests {
use super::*;
use core::f32::*;
#[test]
fn sanity_check() {
assert_eq!(sqrtf(f32!(100.0)), f32!(10.0));
assert_eq!(sqrtf(f32!(4.0)), f32!(2.0));
}
#[test]
fn spec_tests() {
assert!(sqrtf(f32!(-1.0)).to_native_f32().is_nan());
assert!(sqrtf(f32!(NAN)).to_native_f32().is_nan());
for f in [0.0, -0.0, INFINITY].iter().copied() {
assert_eq!(sqrtf(F32::from_native_f32(f)).to_native_f32(), f);
}
}
}