// Copyright 2012 Google Inc.
// Copyright 2020 Yevhenii Reizner
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use super::Scalar64;

#[cfg(all(not(feature = "std"), feature = "no-std-float"))]
use ruisa_path::NoStdFloat;

pub fn push_valid_ts(s: &[f64], real_roots: usize, t: &mut [f64]) -> usize {
    let mut found_roots = 0;
    'outer: for index in 0..real_roots {
        let mut t_value = s[index];
        if t_value.approximately_zero_or_more() && t_value.approximately_one_or_less() {
            t_value = t_value.bound(0.0, 1.0);

            for idx2 in 0..found_roots {
                if t[idx2].approximately_equal(t_value) {
                    continue 'outer;
                }
            }

            t[found_roots] = t_value;
            found_roots += 1;
        }
    }

    found_roots
}

// note: caller expects multiple results to be sorted smaller first
// note: http://en.wikipedia.org/wiki/Loss_of_significance has an interesting
//  analysis of the quadratic equation, suggesting why the following looks at
//  the sign of B -- and further suggesting that the greatest loss of precision
//  is in b squared less two a c
pub fn roots_valid_t(a: f64, b: f64, c: f64, t: &mut [f64]) -> usize {
    let mut s = [0.0; 3];
    let real_roots = roots_real(a, b, c, &mut s);
    push_valid_ts(&s, real_roots, t)
}

// Numeric Solutions (5.6) suggests to solve the quadratic by computing
//     Q = -1/2(B + sgn(B)Sqrt(B^2 - 4 A C))
// and using the roots
//     t1 = Q / A
//     t2 = C / Q
//
// this does not discard real roots <= 0 or >= 1
pub fn roots_real(a: f64, b: f64, c: f64, s: &mut [f64; 3]) -> usize {
    if a == 0.0 {
        return handle_zero(b, c, s);
    }

    let p = b / (2.0 * a);
    let q = c / a;
    if a.approximately_zero() && (p.approximately_zero_inverse() || q.approximately_zero_inverse())
    {
        return handle_zero(b, c, s);
    }

    // normal form: x^2 + px + q = 0
    let p2 = p * p;
    if !p2.almost_dequal_ulps(q) && p2 < q {
        return 0;
    }

    let mut sqrt_d = 0.0;
    if p2 > q {
        sqrt_d = (p2 - q).sqrt();
    }

    s[0] = sqrt_d - p;
    s[1] = -sqrt_d - p;
    1 + usize::from(!s[0].almost_dequal_ulps(s[1]))
}

fn handle_zero(b: f64, c: f64, s: &mut [f64; 3]) -> usize {
    if b.approximately_zero() {
        s[0] = 0.0;
        (c == 0.0) as usize
    } else {
        s[0] = -c / b;
        1
    }
}
