use scirs2_core::numeric::Complex64;
use scirs2_core::numeric::Zero;
use std::f64::consts::PI;
use crate::error::{SpecialError, SpecialResult};
#[allow(dead_code)]
pub fn fresnel(x: f64) -> SpecialResult<(f64, f64)> {
if x.is_nan() {
return Err(SpecialError::DomainError(
"NaN input to fresnel".to_string(),
));
}
if x == 0.0 {
return Ok((0.0, 0.0));
}
if x.abs() > 6.0 {
let (s, c) = fresnel_asymptotic(x)?;
return Ok((s, c));
}
fresnel_power_series(x)
}
#[allow(dead_code)]
pub fn fresnel_complex(z: Complex64) -> SpecialResult<(Complex64, Complex64)> {
if z.is_nan() {
return Err(SpecialError::DomainError(
"NaN input to fresnel_complex".to_string(),
));
}
if z.is_zero() {
return Ok((Complex64::new(0.0, 0.0), Complex64::new(0.0, 0.0)));
}
if z.norm() > 6.0 {
let (s, c) = fresnel_complex_asymptotic(z)?;
return Ok((s, c));
}
fresnel_complex_power_series(z)
}
#[allow(dead_code)]
fn fresnel_power_series(x: f64) -> SpecialResult<(f64, f64)> {
let sign = x.signum();
let x = x.abs();
if x < 1e-100 {
return Ok((0.0, 0.0));
}
let pi_half = PI / 2.0;
let x2 = x * x;
let x4 = x2 * x2;
if x < 0.5 {
let mut s = 0.0;
let mut term = x * x2 / 6.0; let mut prev_s = 0.0;
let mut num_equal_terms_s = 0;
for k in 0..35 {
if k > 0 {
let denom =
(4 * k + 3) as f64 * (4 * k + 2) as f64 * (4 * k + 1) as f64 * (4 * k) as f64;
let factor = -pi_half * x4 / denom;
term *= factor;
}
if !term.is_finite() {
break;
}
s += term;
let abs_term = term.abs();
let abs_s = s.abs().max(1e-300);
if abs_term < 1e-15 || abs_term < 1e-15 * abs_s {
break;
}
if (s - prev_s).abs() < 1e-15 * abs_s {
num_equal_terms_s += 1;
if num_equal_terms_s > 3 {
break;
}
} else {
num_equal_terms_s = 0;
}
prev_s = s;
}
let mut c = 0.0;
let mut term = x; let mut prev_c = 0.0;
let mut num_equal_terms_c = 0;
for k in 0..35 {
if k > 0 {
let denom =
(4 * k + 2) as f64 * (4 * k + 1) as f64 * (4 * k) as f64 * (4 * k - 1) as f64;
if denom == 0.0 {
continue;
}
let factor = -pi_half * x4 / denom;
term *= factor;
}
if !term.is_finite() {
break;
}
c += term;
let abs_term = term.abs();
let abs_c = c.abs().max(1e-300);
if abs_term < 1e-15 || abs_term < 1e-15 * abs_c {
break;
}
if (c - prev_c).abs() < 1e-15 * abs_c {
num_equal_terms_c += 1;
if num_equal_terms_c > 3 {
break;
}
} else {
num_equal_terms_c = 0;
}
prev_c = c;
}
Ok((sign * s, sign * c))
} else {
let z = pi_half * x2;
let sin_z = z.sin();
let cos_z = z.cos();
let mut f_sum = 0.0;
let mut g_sum = 0.0;
let mut term_f = 1.0;
let mut term_g = 1.0;
for k in 1..25 {
let f_factor = -1.0 / ((2 * k - 1) as f64 * z);
term_f *= f_factor;
f_sum += term_f;
let g_factor = -1.0 / (2.0 * k as f64 * z);
term_g *= g_factor;
g_sum += term_g;
if term_f.abs() < 1e-15 && term_g.abs() < 1e-15 {
break;
}
}
let s = 0.5 - (cos_z * (0.5 + f_sum) + sin_z * g_sum) / (PI * x);
let c = 0.5 - (sin_z * (0.5 + f_sum) - cos_z * g_sum) / (PI * x);
Ok((sign * s, sign * c))
}
}
#[allow(dead_code)]
fn fresnel_asymptotic(x: f64) -> SpecialResult<(f64, f64)> {
let sign = x.signum();
let x = x.abs();
if x > 1e100 {
return Ok((sign * 0.5, sign * 0.5));
}
let z = if x > 1e7 {
let scaled_x = x / 1e7;
PI * scaled_x * scaled_x * 1e14 / 2.0
} else {
PI * x * x / 2.0
};
let reduced_z = if z > 1e10 {
let two_pi = 2.0 * PI;
z % two_pi
} else {
z
};
let sin_z = reduced_z.sin();
let cos_z = reduced_z.cos();
if x > 20.0 {
let f_first_term = 1.0 / (PI * x);
let g_first_term = 1.0 / (PI * 3.0 * 2.0 * z);
let s = 0.5 - f_first_term * cos_z - g_first_term * sin_z;
let c = 0.5 + f_first_term * sin_z - g_first_term * cos_z;
return Ok((sign * s, sign * c));
}
let z2 = z * z;
let z2_inv = 1.0 / z2;
let mut f = 1.0 / (PI * x);
let mut g = 0.0;
let mut prev_f = f;
let mut prev_g = g;
let mut num_stable_terms = 0;
for k in 1..25 {
let k_f64 = k as f64;
let mut z2_pow_k: f64 = z2_inv; for _ in 1..k {
z2_pow_k *= z2_inv;
if z2_pow_k.abs() < 1e-300 {
break; }
}
let f_term =
if k % 2 == 1 { -1.0 } else { 1.0 } * (4.0 * k_f64 - 1.0) * z2_pow_k / (PI * x);
let g_term = if k % 2 == 1 { -1.0 } else { 1.0 } * (4.0 * k_f64 + 1.0) * z2_pow_k
/ ((2.0 * k_f64 + 1.0) * PI);
f += f_term;
g += g_term;
let abs_tol = 1e-15;
let f_rel_tol = 1e-15 * f.abs().max(1e-300);
let g_rel_tol = 1e-15 * g.abs().max(1e-300);
if f_term.abs() < abs_tol && g_term.abs() < abs_tol {
break; }
if f_term.abs() < f_rel_tol && g_term.abs() < g_rel_tol {
break; }
if (f - prev_f).abs() < f_rel_tol && (g - prev_g).abs() < g_rel_tol {
num_stable_terms += 1;
if num_stable_terms > 2 {
break; }
} else {
num_stable_terms = 0;
}
if f_term.abs() > 100.0 * prev_f.abs() || g_term.abs() > 100.0 * prev_g.abs() {
f = prev_f;
g = prev_g;
break;
}
prev_f = f;
prev_g = g;
}
let s = 0.5 - f * cos_z - g * sin_z;
let c = 0.5 + f * sin_z - g * cos_z;
Ok((sign * s, sign * c))
}
#[allow(dead_code)]
fn fresnel_complex_power_series(z: Complex64) -> SpecialResult<(Complex64, Complex64)> {
if z.norm() < 1e-100 {
return Ok((Complex64::new(0.0, 0.0), Complex64::new(0.0, 0.0)));
}
let z2 = z * z;
let z4 = z2 * z2;
let pi_half = Complex64::new(PI / 2.0, 0.0);
if z.norm() < 0.5 {
let mut s = Complex64::new(0.0, 0.0);
let mut term = z * z2 / 3.0; let mut prev_s = Complex64::new(0.0, 0.0);
let mut num_equal_terms_s = 0;
for k in 0..45 {
if k > 0 {
let denom =
(4 * k + 3) as f64 * (4 * k + 2) as f64 * (4 * k + 1) as f64 * (4 * k) as f64;
let factor = -pi_half * z4 / denom;
term *= factor;
}
if !term.is_finite() {
break;
}
s += term;
let norm_term = term.norm();
let norm_s = s.norm().max(1e-300);
if norm_term < 1e-15 || norm_term < 1e-15 * norm_s {
break;
}
if (s - prev_s).norm() < 1e-15 * norm_s {
num_equal_terms_s += 1;
if num_equal_terms_s > 3 {
break;
}
} else {
num_equal_terms_s = 0;
}
prev_s = s;
}
let mut c = Complex64::new(0.0, 0.0);
let mut term = z; let mut prev_c = Complex64::new(0.0, 0.0);
let mut num_equal_terms_c = 0;
for k in 0..45 {
if k > 0 {
if k == 1 {
let denom = (4 * k + 2) as f64 * (4 * k + 1) as f64 * (4 * k) as f64 * 3.0;
let factor = -pi_half * z4 / denom;
term *= factor;
} else {
let denom = (4 * k + 2) as f64
* (4 * k + 1) as f64
* (4 * k) as f64
* (4 * k - 1) as f64;
let factor = -pi_half * z4 / denom;
term *= factor;
}
}
if !term.is_finite() {
break;
}
c += term;
let norm_term = term.norm();
let norm_c = c.norm().max(1e-300);
if norm_term < 1e-15 || norm_term < 1e-15 * norm_c {
break;
}
if (c - prev_c).norm() < 1e-15 * norm_c {
num_equal_terms_c += 1;
if num_equal_terms_c > 3 {
break;
}
} else {
num_equal_terms_c = 0;
}
prev_c = c;
}
Ok((s, c))
} else {
let pi_z2_half = pi_half * z2;
let sin_pi_z2_half = pi_z2_half.sin();
let cos_pi_z2_half = pi_z2_half.cos();
let mut f_sum = Complex64::new(0.0, 0.0);
let mut g_sum = Complex64::new(0.0, 0.0);
let mut term_f = Complex64::new(1.0, 0.0);
let mut term_g = Complex64::new(1.0, 0.0);
for k in 1..35 {
let f_factor = Complex64::new(-1.0, 0.0) / ((2.0 * k as f64 - 1.0) * pi_z2_half);
term_f *= f_factor;
let g_factor = Complex64::new(-1.0, 0.0) / (2.0 * k as f64 * pi_z2_half);
term_g *= g_factor;
if term_f.is_finite() {
f_sum += term_f;
}
if term_g.is_finite() {
g_sum += term_g;
}
if term_f.norm() < 1e-15 && term_g.norm() < 1e-15 {
break;
}
}
let half = Complex64::new(0.5, 0.0);
let pi_z_inv = Complex64::new(1.0 / (PI * z.norm()), 0.0) * (z / z.norm()).conj();
let s = half - (cos_pi_z2_half * (half + f_sum) + sin_pi_z2_half * g_sum) * pi_z_inv;
let c = half - (sin_pi_z2_half * (half + f_sum) - cos_pi_z2_half * g_sum) * pi_z_inv;
Ok((s, c))
}
}
#[allow(dead_code)]
fn fresnel_complex_asymptotic(z: Complex64) -> SpecialResult<(Complex64, Complex64)> {
if !z.is_finite() {
return Err(SpecialError::DomainError(
"Infinite or NaN input to fresnel_complex_asymptotic".to_string(),
));
}
if z.norm() > 1e100 {
return Ok((Complex64::new(0.5, 0.0), Complex64::new(0.5, 0.0)));
}
let pi_z2_half = if z.norm() > 1e7 {
let scaled_z = z / 1e7;
PI * scaled_z * scaled_z * 1e14 / 2.0
} else {
PI * z * z / 2.0
};
let reduced_pi_z2_half = if pi_z2_half.norm() > 1e10 {
let two_pi = Complex64::new(2.0 * PI, 0.0);
let n = (pi_z2_half / two_pi).re.floor();
pi_z2_half - two_pi * Complex64::new(n, 0.0)
} else {
pi_z2_half
};
let sin_pi_z2_half = reduced_pi_z2_half.sin();
let cos_pi_z2_half = reduced_pi_z2_half.cos();
if z.norm() > 20.0 {
let f_first_term = Complex64::new(1.0, 0.0) / (PI * z);
let g_first_term = f_first_term / (3.0 * pi_z2_half);
let half = Complex64::new(0.5, 0.0);
let s = half - f_first_term * cos_pi_z2_half - g_first_term * sin_pi_z2_half;
let c = half + f_first_term * sin_pi_z2_half - g_first_term * cos_pi_z2_half;
return Ok((s, c));
}
let pi_z2_half_sq = pi_z2_half * pi_z2_half;
let mut f = Complex64::new(1.0, 0.0) / (PI * z);
let mut g = Complex64::new(0.0, 0.0);
let mut prev_f = f;
let mut prev_g = g;
let mut num_stable_terms = 0;
for k in 1..20 {
let k_f64 = k as f64;
let mut pi_z2_half_sq_pow_k = Complex64::new(1.0, 0.0);
for _ in 0..k {
pi_z2_half_sq_pow_k /= pi_z2_half_sq;
if !pi_z2_half_sq_pow_k.is_finite() || pi_z2_half_sq_pow_k.norm() < 1e-300 {
break;
}
}
let sign = if k % 2 == 1 { -1.0 } else { 1.0 };
let f_term = sign * (4.0 * k_f64 - 1.0) * pi_z2_half_sq_pow_k / (PI * z);
let g_term = sign * (4.0 * k_f64 + 1.0) * pi_z2_half_sq_pow_k / ((2.0 * k_f64 + 1.0) * PI);
if f_term.is_finite() {
f += f_term;
}
if g_term.is_finite() {
g += g_term;
}
let f_norm = f_term.norm();
let g_norm = g_term.norm();
let f_sum_norm = f.norm().max(1e-300);
let g_sum_norm = g.norm().max(1e-300);
if f_norm < 1e-15 && g_norm < 1e-15 {
break; }
if f_norm < 1e-15 * f_sum_norm && g_norm < 1e-15 * g_sum_norm {
break; }
if (f - prev_f).norm() < 1e-15 * f_sum_norm && (g - prev_g).norm() < 1e-15 * g_sum_norm {
num_stable_terms += 1;
if num_stable_terms > 2 {
break; }
} else {
num_stable_terms = 0;
}
if f_norm > 100.0 * prev_f.norm() || g_norm > 100.0 * prev_g.norm() {
f = prev_f;
g = prev_g;
break;
}
prev_f = f;
prev_g = g;
}
let half = Complex64::new(0.5, 0.0);
let s = half - f * cos_pi_z2_half - g * sin_pi_z2_half;
let c = half + f * sin_pi_z2_half - g * cos_pi_z2_half;
if !s.is_finite() || !c.is_finite() {
let s_approx = Complex64::new(0.5, 0.0);
let c_approx = Complex64::new(0.5, 0.0);
return Ok((s_approx, c_approx));
}
Ok((s, c))
}
#[allow(dead_code)]
pub fn fresnels(x: f64) -> SpecialResult<f64> {
let (s, _) = fresnel(x)?;
Ok(s)
}
#[allow(dead_code)]
pub fn fresnelc(x: f64) -> SpecialResult<f64> {
let (_, c) = fresnel(x)?;
Ok(c)
}
#[allow(dead_code)]
pub fn mod_fresnel_plus(x: f64) -> SpecialResult<(Complex64, Complex64)> {
if x.is_nan() {
return Err(SpecialError::DomainError(
"NaN input to mod_fresnel_plus".to_string(),
));
}
if x.abs() < 1e-100 {
let sqrt_pi = PI.sqrt();
let exp_i_pi_4 = Complex64::new(1.0, 0.0) * Complex64::new(0.5, 0.5).sqrt();
let f_plus_0 = sqrt_pi * exp_i_pi_4 / 2.0;
let k_plus_0 = Complex64::new(0.5, 0.0);
return Ok((f_plus_0, k_plus_0));
}
if x.abs() > 1e100 {
let zero = Complex64::new(0.0, 0.0);
return Ok((zero, zero));
}
let z = x.abs();
let (s, c) = fresnel(z)?;
let sqrt_pi = PI.sqrt();
let sqrt_pi_inv = 1.0 / sqrt_pi;
let phase = if z > 1e7 {
let scaled_z = z / 1e7;
let scaled_z_sq = scaled_z * scaled_z * 1e14;
Complex64::new(0.0, scaled_z_sq + PI / 4.0)
} else {
Complex64::new(0.0, z * z + PI / 4.0)
};
let reduced_phase = if phase.im.abs() > 100.0 {
let two_pi = 2.0 * PI;
let n = (phase.im / two_pi).floor();
Complex64::new(phase.re, phase.im - n * two_pi)
} else {
phase
};
let exp_phase = reduced_phase.exp();
let exp_i_pi_4 = Complex64::new(0.5, 0.5).sqrt();
let f_plus = if x >= 0.0 {
let halfminus_c = if z > 10.0 {
let _z_sq = z * z;
let pi_z = PI * z;
1.0 / (2.0 * pi_z) * z.cos() } else {
0.5 - c
};
let minus_s = if z > 10.0 {
let _z_sq = z * z;
let pi_z = PI * z;
-0.5 + 1.0 / (2.0 * pi_z) * z.sin() } else {
-s
};
let halfminus_cminus_is = Complex64::new(halfminus_c, minus_s);
halfminus_cminus_is * sqrt_pi * exp_i_pi_4
} else {
let half_plus_c = if z > 10.0 {
let _z_sq = z * z;
let pi_z = PI * z;
0.5 + 1.0 / (2.0 * pi_z) * z.cos()
} else {
0.5 + c
};
let plus_s = if z > 10.0 {
let _z_sq = z * z;
let pi_z = PI * z;
0.5 - 1.0 / (2.0 * pi_z) * z.sin()
} else {
s
};
let half_plus_c_plus_is = Complex64::new(half_plus_c, plus_s);
half_plus_c_plus_is * sqrt_pi * exp_i_pi_4
};
let k_plus_unnormalized = exp_phase.conj() * f_plus;
let k_plus = k_plus_unnormalized * sqrt_pi_inv;
if !f_plus.is_finite() || !k_plus.is_finite() {
if x.abs() > 10.0 {
let decay_factor = 1.0 / x.abs();
let f_plus_approx = Complex64::new(decay_factor, decay_factor);
let k_plus_approx = Complex64::new(decay_factor, -decay_factor) * sqrt_pi_inv;
return Ok((f_plus_approx, k_plus_approx));
}
}
Ok((f_plus, k_plus))
}
#[allow(dead_code)]
pub fn mod_fresnelminus(x: f64) -> SpecialResult<(Complex64, Complex64)> {
if x.is_nan() {
return Err(SpecialError::DomainError(
"NaN input to mod_fresnelminus".to_string(),
));
}
if x.abs() < 1e-100 {
let sqrt_pi = PI.sqrt();
let expminus_i_pi_4 = Complex64::new(1.0, 0.0) * Complex64::new(0.5, -0.5).sqrt();
let fminus_0 = sqrt_pi * expminus_i_pi_4 / 2.0;
let kminus_0 = Complex64::new(0.5, 0.0);
return Ok((fminus_0, kminus_0));
}
if x.abs() > 1e100 {
let zero = Complex64::new(0.0, 0.0);
return Ok((zero, zero));
}
let z = x.abs();
let (s, c) = fresnel(z)?;
let sqrt_pi = PI.sqrt();
let sqrt_pi_inv = 1.0 / sqrt_pi;
let phase = if z > 1e7 {
let scaled_z = z / 1e7;
let scaled_z_sq = scaled_z * scaled_z * 1e14;
Complex64::new(0.0, scaled_z_sq + PI / 4.0)
} else {
Complex64::new(0.0, z * z + PI / 4.0)
};
let reduced_phase = if phase.im.abs() > 100.0 {
let two_pi = 2.0 * PI;
let n = (phase.im / two_pi).floor();
Complex64::new(phase.re, phase.im - n * two_pi)
} else {
phase
};
let exp_phase = reduced_phase.exp();
let expminus_i_pi_4 = Complex64::new(0.5, -0.5).sqrt();
let fminus = if x >= 0.0 {
let halfminus_c = if z > 10.0 {
let pi_z = PI * z;
1.0 / (2.0 * pi_z) * z.cos() } else {
0.5 - c
};
let plus_s = if z > 10.0 {
let pi_z = PI * z;
0.5 - 1.0 / (2.0 * pi_z) * z.sin() } else {
s
};
let halfminus_c_plus_is = Complex64::new(halfminus_c, plus_s);
halfminus_c_plus_is * sqrt_pi * expminus_i_pi_4
} else {
let half_plus_c = if z > 10.0 {
let pi_z = PI * z;
0.5 + 1.0 / (2.0 * pi_z) * z.cos()
} else {
0.5 + c
};
let minus_s = if z > 10.0 {
let pi_z = PI * z;
-0.5 + 1.0 / (2.0 * pi_z) * z.sin()
} else {
-s
};
let half_plus_cminus_is = Complex64::new(half_plus_c, minus_s);
half_plus_cminus_is * sqrt_pi * expminus_i_pi_4
};
let kminus_unnormalized = exp_phase * fminus;
let kminus = kminus_unnormalized * sqrt_pi_inv;
if !fminus.is_finite() || !kminus.is_finite() {
if x.abs() > 10.0 {
let decay_factor = 1.0 / x.abs();
let fminus_approx = Complex64::new(decay_factor, -decay_factor);
let kminus_approx = Complex64::new(decay_factor, decay_factor) * sqrt_pi_inv;
return Ok((fminus_approx, kminus_approx));
}
}
Ok((fminus, kminus))
}