pub type Parts = (f64, f64);
macro_rules! narrow_ops {
($t:ty, $mul:ident) => {
fn $mul(mut a: $t, mut b: $t, mut c: $t, mut d: $t) -> ($t, $t) {
let (ac, bd, ad, bc) = (a * c, b * d, a * d, b * c);
let mut x = ac - bd;
let mut y = ad + bc;
if x.is_nan() && y.is_nan() {
let mut recalc = false;
let unit = |v: $t| if v.is_infinite() { 1.0 } else { 0.0 as $t }.copysign(v);
let tame = |v: $t| {
if v.is_nan() {
(0.0 as $t).copysign(v)
} else {
v
}
};
if a.is_infinite() || b.is_infinite() {
a = unit(a);
b = unit(b);
c = tame(c);
d = tame(d);
recalc = true;
}
if c.is_infinite() || d.is_infinite() {
c = unit(c);
d = unit(d);
a = tame(a);
b = tame(b);
recalc = true;
}
if !recalc
&& (ac.is_infinite()
|| bd.is_infinite()
|| ad.is_infinite()
|| bc.is_infinite())
{
a = tame(a);
b = tame(b);
c = tame(c);
d = tame(d);
recalc = true;
}
if recalc {
let inf = <$t>::INFINITY;
x = inf * (a * c - b * d);
y = inf * (a * d + b * c);
}
}
(x, y)
}
};
}
narrow_ops!(f32, mul_parts_f32);
narrow_ops!(f64, mul_parts_f64);
pub fn mul((a, b): Parts, (c, d): Parts) -> Parts {
mul_parts_f64(a, b, c, d)
}
pub fn mul_f32((a, b): Parts, (c, d): Parts) -> Parts {
let (x, y) = mul_parts_f32(a as f32, b as f32, c as f32, d as f32);
(f64::from(x), f64::from(y))
}
pub fn div((a, b): Parts, (c, d): Parts) -> Parts {
let (x, y) = if abs(c) < abs(d) {
let ratio = c / d;
let denom = c * ratio + d;
if abs(ratio) > f64::MIN_POSITIVE {
((a * ratio + b) / denom, (b * ratio - a) / denom)
} else {
(((a / d) * c + b) / denom, ((b / d) * c - a) / denom)
}
} else {
let ratio = d / c;
let denom = d * ratio + c;
if abs(ratio) > f64::MIN_POSITIVE {
((b * ratio + a) / denom, (b - a * ratio) / denom)
} else {
(((b / c) * d + a) / denom, (b - (a / c) * d) / denom)
}
};
recover((a, b), (c, d), (x, y))
}
pub fn div_f32((a, b): Parts, (c, d): Parts) -> Parts {
let (a, b) = (f64::from(a as f32), f64::from(b as f32));
let (c, d) = (f64::from(c as f32), f64::from(d as f32));
let denom = c * c + d * d;
let quotient = ((a * c + b * d) / denom, (b * c - a * d) / denom);
let (x, y) = recover((a, b), (c, d), quotient);
(f64::from(x as f32), f64::from(y as f32))
}
fn recover((a, b): Parts, (c, d): Parts, (x, y): Parts) -> Parts {
if !(x.is_nan() && y.is_nan()) {
return (x, y);
}
let inf = f64::INFINITY;
let unit = |v: f64| if v.is_infinite() { 1.0 } else { 0.0f64 }.copysign(v);
if c == 0.0 && d == 0.0 && (!a.is_nan() || !b.is_nan()) {
let scale = inf.copysign(c);
return (scale * a, scale * b);
}
if (a.is_infinite() || b.is_infinite()) && c.is_finite() && d.is_finite() {
let (a, b) = (unit(a), unit(b));
return (inf * (a * c + b * d), inf * (b * c - a * d));
}
if (c.is_infinite() || d.is_infinite()) && a.is_finite() && b.is_finite() {
let (c, d) = (unit(c), unit(d));
return (0.0 * (a * c + b * d), 0.0 * (b * c - a * d));
}
(x, y)
}
fn abs(x: f64) -> f64 {
f64::from_bits(x.to_bits() & !(1u64 << 63))
}