use num::complex::Complex;
use crate::cln::CLn;
use crate::{Li, Li0, Li1, Li2, Li3, Li4, Li5, Li6};
use super::eta::neg_eta;
use super::fac::{fac, inv_fac};
use super::harmonic::harmonic;
use super::zeta::zeta;
pub fn cli(n: i32, z: Complex<f64>) -> Complex<f64> {
if z.is_nan() {
Complex::new(f64::NAN, f64::NAN)
} else if z.is_infinite() {
Complex::new(f64::NEG_INFINITY, 0.0)
} else if z.im == 0.0 {
if z.re <= 1.0 || n <= 0 {
Complex::new(z.re.li(n), z.im)
} else { let l = z.re.ln();
Complex::new(z.re.li(n), -std::f64::consts::PI*inv_fac(n - 1)*l.powi(n - 1))
}
} else if n < -1 {
let c = 4.0*std::f64::consts::PI*std::f64::consts::PI;
let l2 = z.cln().norm_sqr();
if c*z.norm_sqr() < l2 {
li_series(n, z)
} else if l2 < 0.512*0.512*c {
li_unity_neg(n, z)
} else {
let sqrtz = z.sqrt();
2.0_f64.powi(n - 1)*(cli(n, sqrtz) + cli(n, -sqrtz))
}
} else if n == -1 {
z/((1.0 - z)*(1.0 - z))
} else if n == 0 {
z.li0()
} else if n == 1 {
z.li1()
} else if n == 2 {
z.li2()
} else if n == 3 {
z.li3()
} else if n == 4 {
z.li4()
} else if n == 5 {
z.li5()
} else if n == 6 {
z.li6()
} else if z.norm_sqr() <= 0.75*0.75 {
li_series(n, z)
} else if z.norm_sqr() >= 1.4*1.4 {
let sgn = if is_even(n) { -1.0 } else { 1.0 };
sgn*li_series(n, 1.0/z) + li_rest(n, z)
} else {
li_unity_pos(n, z)
}
}
fn is_even(x: i32) -> bool {
x & 1 == 0
}
fn li_rest(n: i32, z: Complex<f64>) -> Complex<f64> {
let lnz = (-z).cln();
let lnz2 = lnz*lnz;
let kmax = if is_even(n) { n/2 } else { (n - 1)/2 };
let mut p = if is_even(n) { Complex::new(1.0, 0.0) } else { lnz };
let mut sum = Complex::new(0.0, 0.0);
for k in (1..=kmax).rev() {
let ifac = inv_fac(n - 2*k);
if ifac == 0.0 { return 2.0*sum; }
let old_sum = sum;
sum += neg_eta(2*k)*ifac*p;
p *= lnz2;
if sum == old_sum { break; }
}
2.0*sum - p*inv_fac(n)
}
fn li_unity_pos(n: i32, z: Complex<f64>) -> Complex<f64> {
let l = z.cln();
let mut sum = Complex::new(zeta(n), 0.0);
let mut p = Complex::new(1.0, 0.0);
for j in 1..(n - 1) {
p *= l/(j as f64);
let old_sum = sum;
sum += zeta(n - j)*p;
if sum == old_sum { break; }
}
p *= l/((n - 1) as f64);
sum += (harmonic(n - 1) - (-l).cln())*p;
p *= l/(n as f64);
sum += zeta(0)*p;
p *= l/((n + 1) as f64);
sum += zeta(-1)*p;
let l2 = l*l;
for j in ((n + 3)..i32::MAX).step_by(2) {
p *= l2/(j - 1).checked_mul(j).map_or(std::f64::INFINITY, |i| i as f64);
let old_sum = sum;
sum += zeta(n - j)*p;
if sum == old_sum { break; }
}
sum
}
fn li_unity_neg(n: i32, z: Complex<f64>) -> Complex<f64> {
let lnz = z.cln();
let lnz2 = lnz*lnz;
let mut sum = fac(-n)*(-lnz).powi(n - 1);
let (mut k, mut lnzk) = if is_even(n) {
(1, lnz)
} else {
sum += zeta(n);
(2, lnz2)
};
loop {
let term = zeta(n - k)*inv_fac(k)*lnzk;
if !term.is_finite() { break; }
let sum_old = sum;
sum += term;
if sum == sum_old || k >= i32::MAX - 2 { break; }
lnzk *= lnz2;
k += 2;
}
sum
}
fn li_series(n: i32, z: Complex<f64>) -> Complex<f64>
{
let mut sum = z;
let mut zn = z*z;
for k in 2..i32::MAX {
let term = zn/(k as f64).powi(n);
if !term.is_finite() { break; }
let old_sum = sum;
sum += term;
if sum == old_sum { break; }
zn *= z;
}
sum
}