use num_complex::Complex;
use num_traits::{Float, One, Zero};
use std::f64::consts::PI;
use crate::Polynomial;
pub fn dka_method<T: Float + Zero + One>(coefficients: &Polynomial<Complex<T>>) -> Option<Vec<Complex<T>>> {
let mut coeffs = coefficients.0.clone();
while let Some(&c) = coeffs.first() {
if c != Complex::zero() {
break;
}
coeffs.remove(0);
}
let n = coeffs.len();
if n <= 1 {
return None;
}
let leading = coeffs[0];
let monic_coeffs: Vec<Complex<T>> = coeffs.into_iter().map(|x| x / leading).collect();
let z = adaptive_initial_values(&monic_coeffs);
let z = aberth(&monic_coeffs, z);
if monic_coeffs.iter().all(|c| c.im.is_zero()) {
Some(conjugate_symmetrize(z))
} else {
Some(z)
}
}
fn conjugate_symmetrize<T: Float>(mut z: Vec<Complex<T>>) -> Vec<Complex<T>> {
let mut order: Vec<usize> = (0..z.len()).collect();
order.sort_by(|&a, &b| z[b].im.abs().partial_cmp(&z[a].im.abs()).unwrap_or(std::cmp::Ordering::Equal));
let mut done = vec![false; z.len()];
for &i in &order {
if done[i] {
continue;
}
let target = z[i].conj();
let j = (0..z.len())
.filter(|&k| !done[k])
.min_by(|&a, &b| {
let da = (z[a] - target).norm();
let db = (z[b] - target).norm();
da.partial_cmp(&db).unwrap_or(std::cmp::Ordering::Equal)
})
.unwrap();
if j == i {
z[i].im = T::zero();
} else {
let two = T::one() + T::one();
let avg = (z[i] + z[j].conj()) / two;
z[i] = avg;
z[j] = avg.conj();
done[j] = true;
}
done[i] = true;
}
z
}
fn newton_polygon_hull<T: Float>(monic_coeffs: &[Complex<T>]) -> Vec<(usize, f64)> {
let n_len = monic_coeffs.len();
let degree = n_len - 1;
let norm_at = |k: usize| monic_coeffs[degree - k].norm();
let mut pts: Vec<(usize, f64)> = Vec::with_capacity(n_len);
for k in 0..n_len {
let c = norm_at(k).to_f64().unwrap();
if c > 0.0 {
pts.push((k, c.ln()));
}
}
let mut hull: Vec<(usize, f64)> = Vec::new();
for &p in &pts {
while hull.len() >= 2 {
let a = hull[hull.len() - 2];
let b = hull[hull.len() - 1];
let cross = (b.0 as f64 - a.0 as f64) * (p.1 - a.1)
- (b.1 - a.1) * (p.0 as f64 - a.0 as f64);
if cross >= 0.0 { hull.pop(); } else { break; }
}
hull.push(p);
}
hull
}
fn fujiwara_initial_values<T: Float>(monic_coeffs: &[Complex<T>]) -> Vec<Complex<T>> {
fn fujiwara_bound<T: Float>(monic_coeffs: &[Complex<T>]) -> T {
let degree = monic_coeffs.len() - 1;
if degree == 0 {
return T::one();
}
let two = T::from(2.0).unwrap();
let mut max_val = T::zero();
for (k, coeff) in monic_coeffs.iter().enumerate().take(degree).skip(1) {
let abs_coeff = coeff.norm();
if abs_coeff > T::zero() {
let cand = abs_coeff.powf(T::from(1.0 / k as f64).unwrap());
if cand > max_val { max_val = cand; }
}
}
let last = (monic_coeffs[degree].norm() / two)
.powf(T::from(1.0 / degree as f64).unwrap());
if last > max_val { max_val = last; }
two * max_val
}
let degree = monic_coeffs.len() - 1;
let r = fujiwara_bound(monic_coeffs);
let mut z: Vec<Complex<T>> = Vec::with_capacity(degree);
for i in 0..degree {
let angle = T::from(2.0 * PI * (i as f64 + 0.25) / degree as f64).unwrap();
z.push(Complex::from_polar(r, angle)); }
z
}
fn bini_initial_values<T: Float>(monic_coeffs: &[Complex<T>]) -> Vec<Complex<T>> {
let n_len = monic_coeffs.len();
let degree = n_len - 1;
let two_pi = 2.0 * PI;
let sigma = 0.7_f64;
let norm_at = |k: usize| monic_coeffs[degree - k].norm();
let hull = newton_polygon_hull(monic_coeffs);
let mut z: Vec<Complex<T>> = Vec::with_capacity(degree);
let k_min = hull.first().map(|&(k, _)| k).unwrap_or(0);
if k_min > 0 {
let tiny = T::from(1e-8).unwrap();
for l in 0..k_min {
let angle = sigma + two_pi * (l as f64) / (k_min as f64);
z.push(Complex::from_polar(tiny, T::from(angle).unwrap()));
}
}
for w in hull.windows(2) {
let (i, _) = w[0];
let (j, _) = w[1];
let m = j - i;
let r = (norm_at(i) / norm_at(j)).powf(T::from(1.0 / m as f64).unwrap());
let group_rot = two_pi * (i as f64) / (degree as f64);
for l in 0..m {
let angle = sigma + group_rot + two_pi * (l as f64) / (m as f64);
z.push(Complex::from_polar(r, T::from(angle).unwrap()));
}
}
z
}
fn adaptive_initial_values<T: Float>(monic_coeffs: &[Complex<T>]) -> Vec<Complex<T>> {
let hull = newton_polygon_hull(monic_coeffs);
let edges = hull.len().saturating_sub(1);
let has_origin_roots = hull.first().map(|&(k, _)| k > 0).unwrap_or(true);
if edges <= 1 && !has_origin_roots {
fujiwara_initial_values(monic_coeffs)
} else {
bini_initial_values(monic_coeffs)
}
}
fn aberth<T: Float>(monic_coeffs: &[Complex<T>], mut z: Vec<Complex<T>>) -> Vec<Complex<T>> {
fn eval_p_dp<T: Float>(desc: &[Complex<T>], x: Complex<T>) -> (Complex<T>, Complex<T>) {
let mut p = Complex::zero();
let mut dp = Complex::zero();
for &c in desc.iter() { dp = dp * x + p; p = p * x + c; }
(p, dp)
}
fn noise_bound<T: Float>(desc: &[Complex<T>], x: Complex<T>) -> T {
let r = x.norm();
let sum = desc.iter().fold(T::zero(), |acc, c| acc * r + c.norm());
T::from(4 * desc.len()).unwrap() * T::epsilon() * sum
}
let degree = z.len();
const MAX_ITER: usize = 10000;
let tol = T::from(1e-12).unwrap();
const FREEZE_AFTER: usize = 200;
let mut frozen = vec![false; degree];
for iter in 0..MAX_ITER {
let mut converged = true;
let z_old = z.clone(); for i in 0..degree {
if frozen[i] { continue; }
let (p, dp) = eval_p_dp(monic_coeffs, z_old[i]);
if iter >= FREEZE_AFTER && p.norm() <= noise_bound(monic_coeffs, z_old[i]) {
frozen[i] = true;
continue;
}
if dp == Complex::zero() { continue; }
let newton = p / dp;
let mut s = Complex::zero();
for j in 0..degree {
if j != i {
let d = z_old[i] - z_old[j];
if d != Complex::zero() { s = s + Complex::<T>::one() / d; }
}
}
let denom = Complex::<T>::one() - newton * s;
if denom == Complex::zero() { continue; }
let w = newton / denom;
z[i] = z_old[i] - w;
let scale = z_old[i].norm().max(T::one());
if w.norm() > tol * scale { converged = false; }
}
if converged { break; }
}
for _ in 0..5 {
let z_old = z.clone();
let mut improved = false;
for i in 0..degree {
let (p, dp) = eval_p_dp(monic_coeffs, z_old[i]);
if p == Complex::zero() || dp == Complex::zero() { continue; }
let newton = p / dp;
let mut s = Complex::zero();
for j in 0..degree {
let d = z_old[i] - z_old[j];
if j != i && d != Complex::zero() { s = s + Complex::<T>::one() / d; }
}
let candidate = z_old[i] - newton / (Complex::<T>::one() - newton * s);
if eval_p_dp(monic_coeffs, candidate).0.norm() < p.norm() {
z[i] = candidate;
improved = true;
}
}
if !improved { break; }
}
z
}
#[allow(dead_code)]
fn durand_kerner<T: Float>(monic_coeffs: &[Complex<T>], mut z: Vec<Complex<T>>) -> Vec<Complex<T>> {
fn mapping_h<T: Float + One>(coeffs: &[Complex<T>], x: Complex<T>) -> Complex<T> {
let mut ret = Complex::zero();
let mut pow = Complex::one();
for &c in coeffs.iter().rev() {
ret = ret + c * pow;
pow = pow * x;
}
ret
}
fn mapping_dh<T: Float>(z: &[Complex<T>], i: usize) -> Complex<T> {
let zi = z[i];
let mut ret = Complex::one();
for (j, &zj) in z.iter().enumerate() {
if i != j {
ret = ret * (zi - zj);
}
}
ret
}
let degree = z.len();
const MAX_ITER: usize = 10000;
let tol = T::from(1e-12).unwrap();
for _ in 0..MAX_ITER {
let mut converged = true;
let mut new_z = z.clone();
for i in 0..degree {
let h = mapping_h(monic_coeffs, z[i]);
let dh = mapping_dh(&z, i);
if dh == Complex::zero() { continue; }
let delta = h / dh;
new_z[i] = z[i] - delta;
let scale = z[i].norm().max(T::one()); if delta.norm() > tol * scale { converged = false; }
}
z = new_z;
if converged { return z; }
}
z
}