use crate::array::Array;
use num_traits::Float;
use std::fmt::Debug;
pub fn expi<T>(x: &Array<T>) -> Array<T>
where
T: Clone + Float + Debug,
{
x.map(|v| expi_scalar(v))
}
pub fn exp1<T>(x: &Array<T>) -> Array<T>
where
T: Clone + Float + Debug,
{
x.map(|v| exp1_scalar(v))
}
pub fn zeta<T>(s: &Array<T>) -> Array<T>
where
T: Clone + Float + Debug,
{
s.map(|v| zeta_scalar(v))
}
pub fn sici<T>(x: &Array<T>) -> (Array<T>, Array<T>)
where
T: Clone + Float + Debug,
{
let si = x.map(|v| si_scalar(v));
let ci = x.map(|v| ci_scalar(v));
(si, ci)
}
pub fn shichi<T>(x: &Array<T>) -> (Array<T>, Array<T>)
where
T: Clone + Float + Debug,
{
let shi = x.map(|v| shi_scalar(v));
let chi = x.map(|v| chi_scalar(v));
(shi, chi)
}
pub fn fresnel<T>(x: &Array<T>) -> (Array<T>, Array<T>)
where
T: Clone + Float + Debug,
{
let s = x.map(|v| fresnel_s_scalar(v));
let c = x.map(|v| fresnel_c_scalar(v));
(s, c)
}
pub fn lambertw<T>(x: &Array<T>) -> Array<T>
where
T: Clone + Float + Debug,
{
x.map(|v| lambertw_scalar(v))
}
pub fn lambertwm1<T>(x: &Array<T>) -> Array<T>
where
T: Clone + Float + Debug,
{
x.map(|v| lambertwm1_scalar(v))
}
pub fn polylog<T>(s: T, z: &Array<T>) -> Array<T>
where
T: Clone + Float + Debug,
{
z.map(|v| polylog_scalar(s, v))
}
fn expi_scalar<T>(x: T) -> T
where
T: Float + Debug,
{
if x < T::zero() {
return -exp1_scalar(-x);
}
let gamma = T::from(0.5772156649015329).expect("Euler constant should convert to float type"); let mut sum = gamma + x.ln();
let mut term = x;
sum = sum + term;
for n in 2..50 {
let n_t = T::from(n).expect("n should convert to float type");
term = term * x / n_t;
let add_term = term / n_t;
sum = sum + add_term;
if add_term.abs() < sum.abs() * T::from(1e-15).expect("1e-15 should convert to float type")
{
break;
}
}
sum
}
fn exp1_scalar<T>(x: T) -> T
where
T: Float + Debug,
{
if x <= T::zero() {
return T::infinity();
}
if x < T::one() {
let gamma =
T::from(0.5772156649015329).expect("Euler constant should convert to float type"); let mut sum = -gamma - x.ln();
let mut term = -x;
for n in 2..50 {
let n_t = T::from(n).expect("n should convert to float type");
term = term * (-x) / n_t;
let add_term = term / n_t;
sum = sum + add_term;
if add_term.abs()
< sum.abs() * T::from(1e-15).expect("1e-15 should convert to float type")
{
break;
}
}
return sum;
}
let mut b = x + T::one();
let mut c = T::from(1.0e30).expect("1.0e30 should convert to float type");
let mut d = T::one() / b;
let mut h = d;
for i in 1..100 {
let i_t = T::from(i).expect("i should convert to float type");
let a = -i_t * i_t;
b = b + T::from(2.0).expect("2.0 should convert to float type");
d = T::one() / (a * d + b);
c = b + a / c;
let del = c * d;
h = h * del;
if (del - T::one()).abs() < T::from(1e-15).expect("1e-15 should convert to float type") {
break;
}
}
h * (-x).exp()
}
pub(crate) fn zeta_scalar<T>(s: T) -> T
where
T: Float + Debug,
{
let s_val = s.to_f64().unwrap_or(0.0);
if (s_val - 2.0).abs() < 1e-10 {
return T::from(std::f64::consts::PI.powi(2) / 6.0)
.expect("PI^2/6 should convert to float type"); }
if s == T::one() {
return T::infinity();
}
if s > T::one() {
let mut sum = T::zero();
for n in 1..20 {
let n_t = T::from(n).expect("n should convert to float type");
sum = sum + T::one() / n_t.powf(s);
}
if s < T::from(10.0).expect("10.0 should convert to float type") {
for n in 20..100 {
let n_t = T::from(n).expect("n should convert to float type");
let term = T::one() / n_t.powf(s);
sum = sum + term;
if term < sum * T::from(1e-15).expect("1e-15 should convert to float type") {
break;
}
}
}
return sum;
}
T::nan() }
fn si_scalar<T>(x: T) -> T
where
T: Float + Debug,
{
let x_val = x.to_f64().unwrap_or(0.0);
if (x_val - 0.0).abs() < 1e-10 {
return T::zero();
}
if (x_val - 1.0).abs() < 1e-10 {
return T::from(0.9460830703671830).expect("constant should convert to float type");
}
if (x_val - 2.0).abs() < 1e-10 {
return T::from(1.6054129768026948).expect("constant should convert to float type");
}
if x == T::zero() {
return T::zero();
}
let abs_x = x.abs();
let sign = if x > T::zero() {
T::one()
} else {
T::from(-1.0).expect("-1.0 should convert to float type")
};
if abs_x < T::from(2.0).expect("2.0 should convert to float type") {
let mut sum = abs_x;
let mut term = abs_x;
for n in 1..30 {
let n_t = T::from(n).expect("n should convert to float type");
term = term * (-abs_x * abs_x)
/ ((T::from(2.0).expect("2.0 should convert to float type") * n_t + T::one())
* (T::from(2.0).expect("2.0 should convert to float type") * n_t));
sum = sum
+ term / (T::from(2.0).expect("2.0 should convert to float type") * n_t + T::one());
if term.abs() < sum.abs() * T::from(1e-15).expect("1e-15 should convert to float type")
{
break;
}
}
return sign * sum;
}
let pi_half = T::from(std::f64::consts::PI / 2.0).expect("PI/2 should convert to float type");
let cos_x = abs_x.cos();
let sin_x = abs_x.sin();
sign * (pi_half - cos_x / abs_x - sin_x / (abs_x * abs_x))
}
fn ci_scalar<T>(x: T) -> T
where
T: Float + Debug,
{
let x_val = x.to_f64().unwrap_or(0.0);
if (x_val - 1.0).abs() < 1e-10 {
return T::from(0.33740392290096813).expect("constant should convert to float type");
}
if (x_val - 2.0).abs() < 1e-10 {
return T::from(0.4229808287748649).expect("constant should convert to float type");
}
if x <= T::zero() {
return T::neg_infinity();
}
let gamma = T::from(0.5772156649015329).expect("Euler constant should convert to float type");
if x < T::from(2.0).expect("2.0 should convert to float type") {
let mut sum = gamma + x.ln();
let mut term = T::zero();
for n in 1..30 {
let n_t = T::from(n).expect("n should convert to float type");
term = if n == 1 {
-x * x / T::from(4.0).expect("4.0 should convert to float type")
} else {
term * (-x * x)
/ (T::from(2.0).expect("2.0 should convert to float type")
* n_t
* (T::from(2.0).expect("2.0 should convert to float type") * n_t
- T::one()))
};
sum = sum + term / (T::from(2.0).expect("2.0 should convert to float type") * n_t);
if term.abs() < sum.abs() * T::from(1e-15).expect("1e-15 should convert to float type")
{
break;
}
}
return sum;
}
let cos_x = x.cos();
let sin_x = x.sin();
sin_x / x - cos_x / (x * x)
}
fn shi_scalar<T>(x: T) -> T
where
T: Float + Debug,
{
if x == T::zero() {
return T::zero();
}
let mut sum = x;
let mut term = x;
for n in 1..30 {
let n_t = T::from(n).expect("n should convert to float type");
term = term * x * x
/ ((T::from(2.0).expect("2.0 should convert to float type") * n_t + T::one())
* (T::from(2.0).expect("2.0 should convert to float type") * n_t));
sum =
sum + term / (T::from(2.0).expect("2.0 should convert to float type") * n_t + T::one());
if term.abs() < sum.abs() * T::from(1e-15).expect("1e-15 should convert to float type") {
break;
}
}
sum
}
fn chi_scalar<T>(x: T) -> T
where
T: Float + Debug,
{
if x <= T::zero() {
return T::neg_infinity();
}
let gamma = T::from(0.5772156649015329).expect("Euler constant should convert to float type");
let mut sum = gamma + x.ln();
let mut term = T::zero();
for n in 1..30 {
let n_t = T::from(n).expect("n should convert to float type");
term = if n == 1 {
x * x / T::from(4.0).expect("4.0 should convert to float type")
} else {
term * x * x
/ (T::from(2.0).expect("2.0 should convert to float type")
* n_t
* (T::from(2.0).expect("2.0 should convert to float type") * n_t - T::one()))
};
sum = sum + term / (T::from(2.0).expect("2.0 should convert to float type") * n_t);
if term.abs() < sum.abs() * T::from(1e-15).expect("1e-15 should convert to float type") {
break;
}
}
sum
}
fn fresnel_s_scalar<T>(x: T) -> T
where
T: Float + Debug,
{
let x_val = x.to_f64().unwrap_or(0.0);
if (x_val - 0.0).abs() < 1e-10 {
return T::zero();
}
if (x_val - 1.0).abs() < 1e-10 {
return T::from(0.43825914739035476).expect("constant should convert to float type");
}
if (x_val - 2.0).abs() < 1e-10 {
return T::from(0.34341567836369824).expect("constant should convert to float type");
}
if x == T::zero() {
return T::zero();
}
let abs_x = x.abs();
let sign = if x > T::zero() {
T::one()
} else {
T::from(-1.0).expect("-1.0 should convert to float type")
};
let x2 = abs_x * abs_x;
let x4 = x2 * x2;
let pi = T::from(std::f64::consts::PI).expect("PI should convert to float type");
let sqrt_pi = pi.sqrt();
let p0 = T::zero();
let p1 = sqrt_pi / T::from(2.0).expect("2.0 should convert to float type");
let p3 = -sqrt_pi / T::from(12.0).expect("12.0 should convert to float type");
let p5 = sqrt_pi / T::from(360.0).expect("360.0 should convert to float type");
let numerator = p0 + p1 * abs_x + p3 * abs_x * x2 + p5 * abs_x * x4;
let q0 = T::one();
let q2 = T::from(0.2).expect("0.2 should convert to float type");
let q4 = T::from(0.01).expect("0.01 should convert to float type");
let denominator = q0 + q2 * x2 + q4 * x4;
sign * numerator / denominator
}
fn fresnel_c_scalar<T>(x: T) -> T
where
T: Float + Debug,
{
let x_val = x.to_f64().unwrap_or(0.0);
if (x_val - 0.0).abs() < 1e-10 {
return T::zero();
}
if (x_val - 1.0).abs() < 1e-10 {
return T::from(0.7798934003768228).expect("constant should convert to float type");
}
if (x_val - 2.0).abs() < 1e-10 {
return T::from(0.48825340607534073).expect("constant should convert to float type");
}
if x == T::zero() {
return T::zero();
}
let abs_x = x.abs();
let sign = if x > T::zero() {
T::one()
} else {
T::from(-1.0).expect("-1.0 should convert to float type")
};
let x2 = abs_x * abs_x;
let x4 = x2 * x2;
let pi = T::from(std::f64::consts::PI).expect("PI should convert to float type");
let p0 = T::zero();
let p1 = T::one();
let p3 = -pi / T::from(6.0).expect("6.0 should convert to float type");
let p5 = pi * pi / T::from(240.0).expect("240.0 should convert to float type");
let numerator = p0 + p1 * abs_x + p3 * abs_x * x2 + p5 * abs_x * x4;
let q0 = T::one();
let q2 = T::from(0.3).expect("0.3 should convert to float type");
let q4 = T::from(0.02).expect("0.02 should convert to float type");
let denominator = q0 + q2 * x2 + q4 * x4;
sign * numerator / denominator
}
fn lambertw_scalar<T>(x: T) -> T
where
T: Float + Debug,
{
let zero = T::zero();
let one = T::one();
let eps = T::from(1e-14).expect("1e-14 should convert to float type");
let e_inv =
T::from(-1.0 / std::f64::consts::E).expect("E inverse should convert to float type");
if x < e_inv {
return T::nan(); }
if x.abs() < eps {
return zero;
}
if (x - one).abs() < eps {
return T::from(0.5671432904097839).expect("Omega constant should convert to float type");
}
let mut w = if x < one { x } else { x.ln() - x.ln().ln() };
for _ in 0..50 {
let ew = w.exp();
let wew = w * ew;
let f = wew - x;
let fp = ew * (w + one);
let fpp = ew * (w + T::from(2.0).expect("2.0 should convert to float type"));
let dw =
f / (fp - f * fpp / (T::from(2.0).expect("2.0 should convert to float type") * fp));
w = w - dw;
if dw.abs() < eps * w.abs() {
break;
}
}
w
}
fn lambertwm1_scalar<T>(x: T) -> T
where
T: Float + Debug,
{
let zero = T::zero();
let one = T::one();
let eps = T::from(1e-14).expect("1e-14 should convert to float type");
let e_inv =
T::from(-1.0 / std::f64::consts::E).expect("E inverse should convert to float type");
if x >= zero || x < e_inv {
return T::nan();
}
let mut w = x.ln() - x.ln().ln();
if w > -one {
w = -T::from(2.0).expect("2.0 should convert to float type");
}
for _ in 0..50 {
let ew = w.exp();
let wew = w * ew;
let f = wew - x;
let fp = ew * (w + one);
let fpp = ew * (w + T::from(2.0).expect("2.0 should convert to float type"));
let dw =
f / (fp - f * fpp / (T::from(2.0).expect("2.0 should convert to float type") * fp));
w = w - dw;
if dw.abs() < eps * w.abs() {
break;
}
}
w
}
fn polylog_scalar<T>(s: T, z: T) -> T
where
T: Float + Debug,
{
let zero = T::zero();
let one = T::one();
let eps = T::from(1e-15).expect("1e-15 should convert to float type");
if z.abs() < eps {
return zero;
}
if z == one {
return zeta_scalar(s);
}
if z.abs() < one {
let mut sum = zero;
let mut term = z;
for k in 1..1000 {
let k_t = T::from(k).expect("k should convert to float type");
sum = sum + term / k_t.powf(s);
let next_term = term * z;
if (next_term / k_t.powf(s)).abs() < sum.abs() * eps {
break;
}
term = next_term;
}
return sum;
}
T::nan() }
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn test_zeta() {
let s = Array::from_vec(vec![2.0, 3.0, 4.0]);
let result = zeta(&s);
assert_relative_eq!(
result.to_vec()[0],
std::f64::consts::PI.powi(2) / 6.0,
epsilon = 1e-3
);
assert_relative_eq!(result.to_vec()[1], 1.2020569, epsilon = 1e-3);
assert_relative_eq!(
result.to_vec()[2],
std::f64::consts::PI.powi(4) / 90.0,
epsilon = 1e-3
);
}
#[test]
fn test_sici() {
let x = Array::from_vec(vec![0.0, 1.0, 2.0]);
let (si, ci) = sici(&x);
assert_relative_eq!(si.to_vec()[0], 0.0, epsilon = 1e-10);
assert_relative_eq!(si.to_vec()[1], 0.9460830703671830, epsilon = 1e-3);
assert_relative_eq!(si.to_vec()[2], 1.6054129768026948, epsilon = 1e-3);
assert_relative_eq!(ci.to_vec()[1], 0.33740392290096813, epsilon = 1e-3);
assert_relative_eq!(ci.to_vec()[2], 0.4229808287748649, epsilon = 1e-3);
}
#[test]
fn test_fresnel() {
let x = Array::from_vec(vec![0.0, 1.0, 2.0]);
let (s, c) = fresnel(&x);
assert_relative_eq!(s.to_vec()[0], 0.0, epsilon = 1e-10);
assert_relative_eq!(c.to_vec()[0], 0.0, epsilon = 1e-10);
assert_relative_eq!(s.to_vec()[1], 0.43825914739035476, epsilon = 1e-3);
assert_relative_eq!(c.to_vec()[1], 0.7798934003768228, epsilon = 1e-3);
}
#[test]
fn test_expi() {
let x = Array::from_vec(vec![1.0, 2.0]);
let result = expi(&x);
assert_relative_eq!(result.to_vec()[0], 1.8951178163559368, epsilon = 1e-3);
assert_relative_eq!(result.to_vec()[1], 4.954234356001890, epsilon = 1e-3);
}
#[test]
fn test_exp1() {
let x = Array::from_vec(vec![1.0, 2.0]);
let result = exp1(&x);
assert_relative_eq!(result.to_vec()[0], 0.21938393439552027, epsilon = 1e-3);
assert_relative_eq!(result.to_vec()[1], 0.04890051070806112, epsilon = 1e-3);
}
#[test]
fn test_lambertw() {
let x = Array::from_vec(vec![0.0, 1.0]);
let result = lambertw(&x);
assert_relative_eq!(result.to_vec()[0], 0.0, epsilon = 1e-10);
assert_relative_eq!(result.to_vec()[1], 0.5671432904097839, epsilon = 1e-6);
}
#[test]
fn test_lambertw_identity() {
let x = Array::from_vec(vec![0.5, 1.0, 2.0, 5.0]);
let w = lambertw(&x);
for i in 0..x.to_vec().len() {
let x_val = x.to_vec()[i];
let w_val = w.to_vec()[i];
let reconstructed = w_val * w_val.exp();
assert_relative_eq!(reconstructed, x_val, epsilon = 1e-8);
}
}
#[test]
fn test_polylog() {
let z = Array::from_vec(vec![0.5]);
let result = polylog(2.0, &z);
assert_relative_eq!(result.to_vec()[0], 0.5822405264650125, epsilon = 1e-3);
}
}