use crate::math::complex::Complex;
pub fn pj_zpoly1(z: Complex, c: &[Complex], n: usize) -> Complex {
let mut a = c[n];
let mut k = n;
while k > 0 {
let t = a.r;
a.r = c[k - 1].r + z.r * t - z.i * a.i;
a.i = c[k - 1].i + z.r * a.i + z.i * t;
k -= 1;
}
let t = a.r;
let nr = z.r * t - z.i * a.i;
let ni = z.r * a.i + z.i * t;
Complex { r: nr, i: ni }
}
pub fn pj_zpolyd1(z: Complex, c: &[Complex], n: usize) -> (Complex, Complex) {
let mut a = c[n];
let mut b = a;
let mut first = true;
let mut k = n;
while k > 0 {
if first {
first = false;
} else {
let t = b.r;
b.r = a.r + z.r * t - z.i * b.i;
b.i = a.i + z.r * b.i + z.i * t;
}
let t = a.r;
a.r = c[k - 1].r + z.r * t - z.i * a.i;
a.i = c[k - 1].i + z.r * a.i + z.i * t;
k -= 1;
}
let tb = b.r;
let der_r = a.r + z.r * tb - z.i * b.i;
let der_i = a.i + z.r * b.i + z.i * tb;
let der = Complex { r: der_r, i: der_i };
let ta = a.r;
let val_r = z.r * ta - z.i * a.i;
let val_i = z.r * a.i + z.i * ta;
let val = Complex { r: val_r, i: val_i };
(val, der)
}
#[cfg(test)]
mod tests {
use super::*;
fn cmul(a: Complex, b: Complex) -> Complex {
Complex::new(a.r * b.r - a.i * b.i, a.r * b.i + a.i * b.r)
}
#[test]
fn zpoly1_evaluates_z_squared_plus_z_cubed() {
let c = [
Complex::new(0.0, 0.0),
Complex::new(1.0, 0.0),
Complex::new(1.0, 0.0),
];
let z = Complex::new(0.3, 0.4);
let result = pj_zpoly1(z, &c, 2);
assert!((result.r - (-0.187)).abs() < 1e-12);
assert!((result.i - 0.284).abs() < 1e-12);
}
#[test]
fn zpoly1_matches_explicit_cmul_form() {
let c = [
Complex::new(0.0, 0.0),
Complex::new(0.7, -0.2),
Complex::new(-0.5, 0.9),
];
let z = Complex::new(0.15, -0.35);
let result = pj_zpoly1(z, &c, 2);
let inner1 = Complex::new(c[1].r + cmul(z, c[2]).r, c[1].i + cmul(z, c[2]).i);
let inner0 = Complex::new(c[0].r + cmul(z, inner1).r, c[0].i + cmul(z, inner1).i);
let expected = cmul(z, inner0);
assert!((result.r - expected.r).abs() < 1e-12);
assert!((result.i - expected.i).abs() < 1e-12);
}
#[test]
fn zpolyd1_value_matches_zpoly1() {
let c = [
Complex::new(0.0, 0.0),
Complex::new(1.0, 0.0),
Complex::new(1.0, 0.0),
Complex::new(0.4, -0.3),
];
let z = Complex::new(0.2, 0.1);
let (val, _der) = pj_zpolyd1(z, &c, 3);
let expected = pj_zpoly1(z, &c, 3);
assert!((val.r - expected.r).abs() < 1e-12);
assert!((val.i - expected.i).abs() < 1e-12);
}
#[test]
fn zpolyd1_derivative_matches_numeric_difference() {
let c = [
Complex::new(0.0, 0.0),
Complex::new(1.0, 0.0),
Complex::new(1.0, 0.0),
Complex::new(0.4, -0.3),
];
let z = Complex::new(0.2, 0.1);
let (_val, der) = pj_zpolyd1(z, &c, 3);
let h = 1e-6;
let z_plus = Complex::new(z.r + h, z.i);
let z_minus = Complex::new(z.r - h, z.i);
let f_plus = pj_zpoly1(z_plus, &c, 3);
let f_minus = pj_zpoly1(z_minus, &c, 3);
let der_num_r = (f_plus.r - f_minus.r) / (2.0 * h);
let der_num_i = (f_plus.i - f_minus.i) / (2.0 * h);
assert!((der.r - der_num_r).abs() < 1e-6);
assert!((der.i - der_num_i).abs() < 1e-6);
}
}