use super::{fma_available, fma_internal, sqrt};
const SCALE: f64 = f64::from_bits(0x1a70_0000_0000_0000); const LARGE_VAL: f64 = f64::from_bits(0x5fe0_0000_0000_0000); const TINY_VAL: f64 = f64::from_bits(0x2340_0000_0000_0000); const EPS: f64 = f64::from_bits(0x3c90_0000_0000_0000);
#[inline(always)]
fn kernel(ax: f64, ay: f64) -> f64 {
if fma_available() {
let t1 = ay + ay;
let t2 = ax - ay;
if t1 >= ax {
return sqrt(fma_internal(t1, ax, t2 * t2));
}
return sqrt(fma_internal(ax, ax, ay * ay));
}
let mut h = sqrt(ax * ax + ay * ay);
let (t1, t2) = if h <= 2.0 * ay {
let delta = h - ay;
let t1 = ax * (2.0 * delta - ax);
let t2 = (delta - 2.0 * (ax - ay)) * delta;
(t1, t2)
} else {
let delta = h - ax;
let t1 = 2.0 * delta * (ax - 2.0 * ay);
let t2 = (4.0 * delta - ay) * ay + delta * delta;
(t1, t2)
};
h -= (t1 + t2) / (2.0 * h);
h
}
#[inline]
pub fn hypot(x: f64, y: f64) -> f64 {
let mut ax = x.abs();
let mut ay = y.abs();
if ax.is_nan() || ay.is_nan() {
return f64::NAN;
}
if ax.is_infinite() || ay.is_infinite() {
return f64::INFINITY;
}
if ax < ay {
core::mem::swap(&mut ax, &mut ay);
}
if ax == 0.0 {
return 0.0;
}
if ax > LARGE_VAL {
if ay <= ax * EPS {
return ax + ay;
}
return kernel(ax * SCALE, ay * SCALE) / SCALE;
}
if ay < TINY_VAL {
if ax >= ay / EPS {
return ax + ay;
}
let res = kernel(ax / SCALE, ay / SCALE) * SCALE;
return res;
}
if ay <= ax * EPS {
return ax + ay;
}
kernel(ax, ay)
}