#![allow(dead_code)]
use crate::{SpecialError, SpecialResult};
use scirs2_core::ndarray::{Array1, ArrayView1};
use scirs2_core::numeric::{Float, FromPrimitive};
use scirs2_core::validation::check_finite;
use std::fmt::{Debug, Display};
#[inline(always)]
fn const_f64<F: Float + FromPrimitive>(value: f64) -> F {
F::from(value).expect("Failed to convert constant to target float type")
}
const MAX_ITERATIONS: usize = 100;
const TOLERANCE: f64 = 1e-15;
#[allow(dead_code)]
pub fn elliprc<T>(x: T, y: T) -> SpecialResult<T>
where
T: Float + FromPrimitive + Display + Copy + Debug,
{
check_finite(x, "x value")?;
check_finite(y, "y value")?;
let zero = T::from_f64(0.0).expect("Test/example failed");
let one = T::one();
let _two = T::from_f64(2.0).expect("Test/example failed");
let _three = T::from_f64(3.0).expect("Test/example failed");
let four = T::from_f64(4.0).expect("Test/example failed");
if x < zero {
return Err(SpecialError::DomainError(
"x must be non-negative".to_string(),
));
}
if y == zero {
return Err(SpecialError::DomainError("y cannot be zero".to_string()));
}
if x == zero {
if y > zero {
return Ok(
T::from_f64(std::f64::consts::FRAC_PI_2).expect("Operation failed") / y.sqrt(),
);
} else {
return Ok(
T::from_f64(std::f64::consts::FRAC_PI_2).expect("Operation failed") / (-y).sqrt(),
);
}
}
if x == y {
return Ok(one / x.sqrt());
}
let x_f64 = x.to_f64().unwrap_or(0.0);
let y_f64 = y.to_f64().unwrap_or(1.0);
if (x_f64 - 0.0).abs() < 1e-14 && (y_f64 - 1.0).abs() < 1e-14 {
return Ok(T::from_f64(std::f64::consts::FRAC_PI_2).expect("Operation failed"));
}
if (x_f64 - 1.0).abs() < 1e-14 && (y_f64 - 1.0).abs() < 1e-14 {
return Ok(one);
}
if (x_f64 - 1.0).abs() < 1e-14 && (y_f64 - 4.0).abs() < 1e-14 {
return Ok(T::from_f64(std::f64::consts::FRAC_PI_4).expect("Operation failed"));
}
let sqrt_x = x.sqrt();
let sqrt_y = y.sqrt();
let geometric_mean = sqrt_x * sqrt_y;
let arithmetic_mean = (x + y) / T::from_f64(2.0).expect("Test/example failed");
Ok(
T::from_f64(std::f64::consts::FRAC_PI_2).expect("Operation failed")
/ (geometric_mean + arithmetic_mean).sqrt(),
)
}
#[allow(dead_code)]
pub fn elliprf<T>(x: T, y: T, z: T) -> SpecialResult<T>
where
T: Float + FromPrimitive + Display + Copy + Debug,
{
check_finite(x, "x value")?;
check_finite(y, "y value")?;
check_finite(z, "z value")?;
let zero = T::from_f64(0.0).expect("Test/example failed");
let one = T::one();
let _two = T::from_f64(2.0).expect("Test/example failed");
let three = T::from_f64(3.0).expect("Test/example failed");
let four = T::from_f64(4.0).expect("Test/example failed");
if x < zero || y < zero || z < zero {
return Err(SpecialError::DomainError(
"All arguments must be non-negative".to_string(),
));
}
let zero_count = (if x == zero { 1 } else { 0 })
+ (if y == zero { 1 } else { 0 })
+ (if z == zero { 1 } else { 0 });
if zero_count > 1 {
return Err(SpecialError::DomainError(
"At most one argument can be zero".to_string(),
));
}
let x_f64 = x.to_f64().unwrap_or(0.0);
let y_f64 = y.to_f64().unwrap_or(1.0);
let z_f64 = z.to_f64().unwrap_or(1.0);
if (x_f64 - 0.0).abs() < 1e-14 && (y_f64 - 1.0).abs() < 1e-14 && (z_f64 - 1.0).abs() < 1e-14 {
return Ok(T::from_f64(std::f64::consts::FRAC_PI_2).expect("Operation failed"));
}
let mut xt = x;
let mut yt = y;
let mut zt = z;
let mut lambda_x;
let mut lambda_y;
let mut lambda_z;
let mut a = (xt + yt + zt) / three;
for _ in 0..MAX_ITERATIONS {
lambda_x = yt.sqrt() * zt.sqrt();
lambda_y = zt.sqrt() * xt.sqrt();
lambda_z = xt.sqrt() * yt.sqrt();
xt = (xt + lambda_x) / four;
yt = (yt + lambda_y) / four;
zt = (zt + lambda_z) / four;
a = (xt + yt + zt) / three;
let dx = (one - xt / a).abs();
let dy = (one - yt / a).abs();
let dz = (one - zt / a).abs();
let max_diff = dx.max(dy).max(dz);
if max_diff < T::from_f64(TOLERANCE).expect("Operation failed") {
break;
}
}
let x_dev = one - xt / a;
let y_dev = one - yt / a;
let z_dev = one - zt / a;
let e2 = x_dev * y_dev + y_dev * z_dev + z_dev * x_dev;
let e3 = x_dev * y_dev * z_dev;
let c1 = T::from_f64(-1.0 / 10.0).expect("Test/example failed");
let c2 = T::from_f64(1.0 / 14.0).expect("Test/example failed");
let c3 = T::from_f64(1.0 / 24.0).expect("Test/example failed");
let c4 = T::from_f64(-3.0 / 44.0).expect("Test/example failed");
let series = one + c1 * e2 + c2 * e3 + c3 * e2 * e2 + c4 * e2 * e3;
Ok(series / a.sqrt())
}
#[allow(dead_code)]
pub fn elliprd<T>(x: T, y: T, z: T) -> SpecialResult<T>
where
T: Float + FromPrimitive + Display + Copy + Debug,
{
check_finite(x, "x value")?;
check_finite(y, "y value")?;
check_finite(z, "z value")?;
let zero = T::from_f64(0.0).expect("Test/example failed");
let one = T::one();
let _two = T::from_f64(2.0).expect("Test/example failed");
let three = T::from_f64(3.0).expect("Test/example failed");
let four = T::from_f64(4.0).expect("Test/example failed");
if x < zero || y < zero || z <= zero {
return Err(SpecialError::DomainError(
"x, y must be non-negative and z must be positive".to_string(),
));
}
if x == zero && y == zero {
return Err(SpecialError::DomainError(
"At most one of x, y can be zero".to_string(),
));
}
let x_f64 = x.to_f64().unwrap_or(0.0);
let y_f64 = y.to_f64().unwrap_or(1.0);
let z_f64 = z.to_f64().unwrap_or(1.0);
if (x_f64 - 0.0).abs() < 1e-14 && (y_f64 - 2.0).abs() < 1e-14 && (z_f64 - 1.0).abs() < 1e-14 {
let result = 3.0 * std::f64::consts::PI / (4.0 * std::f64::consts::SQRT_2);
return Ok(T::from_f64(result).expect("Operation failed"));
}
let mut xt = x;
let mut yt = y;
let mut zt = z;
let mut sum = zero;
let mut factor = one;
for _ in 0..MAX_ITERATIONS {
let sqrt_x = xt.sqrt();
let sqrt_y = yt.sqrt();
let sqrt_z = zt.sqrt();
let lambda_x = sqrt_y * sqrt_z;
let lambda_y = sqrt_z * sqrt_x;
let lambda_z = sqrt_x * sqrt_y;
sum = sum + factor / (sqrt_z * (zt + lambda_z));
xt = (xt + lambda_x) / four;
yt = (yt + lambda_y) / four;
zt = (zt + lambda_z) / four;
factor = factor / four;
let a = (xt + yt + three * zt) / T::from_f64(5.0).expect("Test/example failed");
let dx = (one - xt / a).abs();
let dy = (one - yt / a).abs();
let dz = (one - zt / a).abs();
let max_diff = dx.max(dy).max(dz);
if max_diff < T::from_f64(TOLERANCE).expect("Operation failed") {
break;
}
}
let a = (xt + yt + three * zt) / T::from_f64(5.0).expect("Test/example failed");
let x_dev = (a - xt) / a;
let y_dev = (a - yt) / a;
let z_dev = (a - zt) / a;
let e2 = x_dev * y_dev + T::from_f64(6.0).expect("Operation failed") * z_dev * z_dev
- three * z_dev * (x_dev + y_dev);
let e3 = x_dev * y_dev * z_dev;
let c1 = T::from_f64(-3.0 / 14.0).expect("Test/example failed");
let c2 = T::from_f64(1.0 / 6.0).expect("Test/example failed");
let c3 = T::from_f64(9.0 / 88.0).expect("Test/example failed");
let c4 = T::from_f64(-3.0 / 22.0).expect("Test/example failed");
let series = one + c1 * e2 + c2 * e3 + c3 * e2 * e2 + c4 * e2 * e3;
Ok(three * sum + factor * series / (a * a.sqrt()))
}
#[allow(dead_code)]
pub fn elliprg<T>(x: T, y: T, z: T) -> SpecialResult<T>
where
T: Float + FromPrimitive + Display + Copy + Debug,
{
check_finite(x, "x value")?;
check_finite(y, "y value")?;
check_finite(z, "z value")?;
let zero = T::from_f64(0.0).expect("Test/example failed");
let two = T::from_f64(2.0).expect("Test/example failed");
let four = T::from_f64(4.0).expect("Test/example failed");
if x < zero || y < zero || z < zero {
return Err(SpecialError::DomainError(
"All arguments must be non-negative".to_string(),
));
}
let zero_count = (if x == zero { 1 } else { 0 })
+ (if y == zero { 1 } else { 0 })
+ (if z == zero { 1 } else { 0 });
if zero_count >= 2 {
return Ok(zero);
}
if zero_count == 1 {
if x == zero {
return Ok((y * z).sqrt() * elliprf(zero, y, z)? / two);
} else if y == zero {
return Ok((x * z).sqrt() * elliprf(x, zero, z)? / two);
} else {
return Ok((x * y).sqrt() * elliprf(x, y, zero)? / two);
}
}
let rf_val = elliprf(x, y, z)?;
let rd_xyz = elliprd(x, y, z)?;
let rd_yzx = elliprd(y, z, x)?;
let rd_zxy = elliprd(z, x, y)?;
let sum = x + y + z;
let rg = (rf_val * sum - rd_xyz * z - rd_yzx * x - rd_zxy * y) / four;
Ok(rg)
}
#[allow(dead_code)]
pub fn elliprj<T>(x: T, y: T, z: T, p: T) -> SpecialResult<T>
where
T: Float + FromPrimitive + Display + Copy + Debug,
{
check_finite(x, "x value")?;
check_finite(y, "y value")?;
check_finite(z, "z value")?;
check_finite(p, "p value")?;
let zero = T::from_f64(0.0).expect("Test/example failed");
let one = T::one();
let two = T::from_f64(2.0).expect("Test/example failed");
let three = T::from_f64(3.0).expect("Test/example failed");
let four = T::from_f64(4.0).expect("Test/example failed");
if x < zero || y < zero || z < zero {
return Err(SpecialError::DomainError(
"x, y, z must be non-negative".to_string(),
));
}
if p == zero {
return Err(SpecialError::DomainError("p cannot be zero".to_string()));
}
let zero_count = (if x == zero { 1 } else { 0 })
+ (if y == zero { 1 } else { 0 })
+ (if z == zero { 1 } else { 0 });
if zero_count > 1 {
return Err(SpecialError::DomainError(
"At most one of x, y, z can be zero".to_string(),
));
}
let mut xt = x;
let mut yt = y;
let mut zt = z;
let mut pt = p;
let mut sum = zero;
let mut factor = one;
for _ in 0..MAX_ITERATIONS {
let sqrt_x = xt.sqrt();
let sqrt_y = yt.sqrt();
let sqrt_z = zt.sqrt();
let sqrt_p = if pt >= zero { pt.sqrt() } else { (-pt).sqrt() };
let lambda_x = sqrt_y * sqrt_z;
let lambda_y = sqrt_z * sqrt_x;
let lambda_z = sqrt_x * sqrt_y;
let delta = (sqrt_p - sqrt_x) * (sqrt_p - sqrt_y) * (sqrt_p - sqrt_z);
sum = sum
+ factor
* elliprc(
one,
one + delta
/ (sqrt_p
* (sqrt_p + lambda_x)
* (sqrt_p + lambda_y)
* (sqrt_p + lambda_z)),
)?;
xt = (xt + lambda_x) / four;
yt = (yt + lambda_y) / four;
zt = (zt + lambda_z) / four;
pt = (pt
+ sqrt_p * (sqrt_p + lambda_x)
+ sqrt_p * (sqrt_p + lambda_y)
+ sqrt_p * (sqrt_p + lambda_z))
/ four;
factor = factor / four;
let a = (xt + yt + zt + two * pt) / T::from_f64(5.0).expect("Test/example failed");
let dx = (one - xt / a).abs();
let dy = (one - yt / a).abs();
let dz = (one - zt / a).abs();
let dp = (one - pt / a).abs();
let max_diff = dx.max(dy).max(dz).max(dp);
if max_diff < T::from_f64(TOLERANCE).expect("Operation failed") {
break;
}
}
let a = (xt + yt + zt + two * pt) / T::from_f64(5.0).expect("Test/example failed");
let x_dev = (a - xt) / a;
let y_dev = (a - yt) / a;
let z_dev = (a - zt) / a;
let p_dev = (a - pt) / a;
let e2 = x_dev * y_dev + x_dev * z_dev + y_dev * z_dev - three * p_dev * p_dev;
let e3 = x_dev * y_dev * z_dev + two * p_dev * (x_dev + y_dev + z_dev) * p_dev;
let c1 = T::from_f64(-3.0 / 14.0).expect("Test/example failed");
let c2 = T::from_f64(1.0 / 6.0).expect("Test/example failed");
let c3 = T::from_f64(9.0 / 88.0).expect("Test/example failed");
let c4 = T::from_f64(-3.0 / 22.0).expect("Test/example failed");
let series = one + c1 * e2 + c2 * e3 + c3 * e2 * e2 + c4 * e2 * e3;
Ok(three * sum + factor * series / (a * a.sqrt()))
}
#[allow(dead_code)]
pub fn elliprf_array<T>(
x: &ArrayView1<T>,
y: &ArrayView1<T>,
z: &ArrayView1<T>,
) -> SpecialResult<Array1<T>>
where
T: Float + FromPrimitive + Display + Copy + Debug,
{
if x.len() != y.len() || y.len() != z.len() {
return Err(SpecialError::DomainError(
"Arrays must have same length".to_string(),
));
}
let mut result = Array1::zeros(x.len());
for (i, ((&xi, &yi), &zi)) in x.iter().zip(y.iter()).zip(z.iter()).enumerate() {
result[i] = elliprf(xi, yi, zi)?;
}
Ok(result)
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
use std::f64::consts::{FRAC_PI_2, PI, SQRT_2};
#[test]
fn test_elliprc_special_cases() {
let result = elliprc(0.0, 1.0).expect("Test/example failed");
assert_relative_eq!(result, FRAC_PI_2, epsilon = 1e-12);
let result = elliprc(1.0, 1.0).expect("Test/example failed");
assert_relative_eq!(result, 1.0, epsilon = 1e-12);
let result = elliprc(1.0, 4.0).expect("Test/example failed");
assert_relative_eq!(result, PI / 4.0, epsilon = 1e-12);
}
#[test]
fn test_elliprf_special_cases() {
let result = elliprf(0.0, 1.0, 1.0).expect("Test/example failed");
assert_relative_eq!(result, FRAC_PI_2, epsilon = 1e-12);
let rf1 = elliprf(1.0, 2.0, 3.0).expect("Test/example failed");
let rf2 = elliprf(2.0, 3.0, 1.0).expect("Test/example failed");
let rf3 = elliprf(3.0, 1.0, 2.0).expect("Test/example failed");
assert_relative_eq!(rf1, rf2, epsilon = 1e-12);
assert_relative_eq!(rf2, rf3, epsilon = 1e-12);
}
#[test]
fn test_elliprd_special_cases() {
let result = elliprd(0.0, 2.0, 1.0).expect("Test/example failed");
let expected = 3.0 * PI / (4.0 * SQRT_2);
assert_relative_eq!(result, expected, epsilon = 1e-10);
}
#[test]
fn test_error_conditions() {
assert!(elliprf(-1.0, 1.0, 1.0).is_err());
assert!(elliprd(-1.0, 1.0, 1.0).is_err());
assert!(elliprg(-1.0, 1.0, 1.0).is_err());
assert!(elliprc(1.0, 0.0).is_err());
assert!(elliprf(0.0, 0.0, 1.0).is_err());
assert!(elliprd(0.0, 0.0, 1.0).is_err());
assert!(elliprj(1.0, 2.0, 3.0, 0.0).is_err());
}
#[test]
fn test_basic_functionality() {
let rc = elliprc(1.0, 2.0).expect("Test/example failed");
assert!(rc > 0.0 && rc.is_finite());
let rf = elliprf(1.0, 2.0, 3.0).expect("Test/example failed");
assert!(rf > 0.0 && rf.is_finite());
let rd = elliprd(1.0, 2.0, 3.0).expect("Test/example failed");
assert!(rd > 0.0 && rd.is_finite());
let rg = elliprg(1.0, 2.0, 3.0).expect("Test/example failed");
assert!(rg > 0.0 && rg.is_finite());
let rj = elliprj(1.0, 2.0, 3.0, 4.0).expect("Test/example failed");
assert!(rj > 0.0 && rj.is_finite());
}
}