use super::super::FloatType;
use super::super::Roots;
pub fn find_roots_cubic_normalized<F: FloatType>(a2: F, a1: F, a0: F) -> Roots<F> {
let _2 = F::from(2i16);
let _3 = F::from(3i16);
let _4 = F::from(4i16);
let _9 = F::from(9i16);
let _18 = F::from(18i16);
let _27 = F::from(27i16);
let _54 = F::from(54i16);
let q = (_3 * a1 - a2 * a2) / _9;
let r = (_9 * a2 * a1 - _27 * a0 - _2 * a2 * a2 * a2) / _54;
let q3 = q * q * q;
let d = q3 + r * r;
let a2_div_3 = a2 / _3;
if d < F::zero() {
let phi_3 = (r / (-q3).sqrt()).acos() / _3;
let sqrt_q_2 = _2 * (-q).sqrt();
Roots::One([sqrt_q_2 * phi_3.cos() - a2_div_3])
.add_new_root(sqrt_q_2 * (phi_3 - F::two_third_pi()).cos() - a2_div_3)
.add_new_root(sqrt_q_2 * (phi_3 + F::two_third_pi()).cos() - a2_div_3)
} else {
let sqrt_d = d.sqrt();
let s = (r + sqrt_d).cbrt();
let t = (r - sqrt_d).cbrt();
if s == t {
if s + t == F::zero() {
Roots::One([s + t - a2_div_3])
} else {
Roots::One([s + t - a2_div_3]).add_new_root(-(s + t) / _2 - a2_div_3)
}
} else {
Roots::One([s + t - a2_div_3])
}
}
}
#[cfg(test)]
mod test {
use super::super::super::*;
#[test]
fn test_find_roots_cubic_normalized() {
assert_eq!(find_roots_cubic_normalized(0f32, 0f32, 0f32), Roots::One([0f32]));
match find_roots_cubic_normalized(0f64, -1f64, 0f64) {
Roots::Three(x) => {
assert_float_array_eq!(1e-15, x, [-1f64, 0f64, 1f64]);
}
_ => {
assert!(false);
}
}
match find_roots_cubic_normalized(1f64, -2f64, 2f64) {
Roots::One(x) => {
assert_float_array_eq!(1e-15, x, [-2.269530842081142770853135f64]);
}
_ => {
assert!(false);
}
}
match find_roots_cubic_normalized(0f64, -3f64, 2f64) {
Roots::Two(x) => {
assert_float_array_eq!(1e-15, x, [-2f64, 1f64]);
}
_ => {
assert!(false);
}
}
match find_roots_cubic_normalized(-2f64, -3f64, 2f64) {
Roots::Three(x) => {
assert_float_array_eq!(
1e-15,
x,
[
-1.342923082777170208054859f64,
0.5293165801288393926136314f64,
2.813606502648330815441228f64
]
);
}
_ => {
assert!(false);
}
}
}
#[test]
fn test_find_roots_cubic_normalized_huge_discriminant() {
match find_roots_cubic_normalized(
0.0126298310280606f64 / -0.000000000000000040410628481035f64,
-0.100896606408756f64 / -0.000000000000000040410628481035f64,
0.0689539597036461f64 / -0.000000000000000040410628481035f64,
) {
Roots::One(x) => {
assert_float_array_eq!(1e-8, x, [312537357195212.4625f64]);
}
_ => {
assert!(false);
}
}
}
}