use ferray_core::Array;
use ferray_core::dimension::Dimension;
use ferray_core::dtype::Element;
use ferray_core::error::FerrayResult;
use num_complex::Complex;
use num_traits::Float;
pub fn real<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<T, D>>
where
T: Element + Float,
Complex<T>: Element,
D: Dimension,
{
let data: Vec<T> = input.iter().map(|c| c.re).collect();
Array::from_vec(input.dim().clone(), data)
}
pub fn imag<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<T, D>>
where
T: Element + Float,
Complex<T>: Element,
D: Dimension,
{
let data: Vec<T> = input.iter().map(|c| c.im).collect();
Array::from_vec(input.dim().clone(), data)
}
pub fn conj<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<Complex<T>, D>>
where
T: Element + Float,
Complex<T>: Element,
D: Dimension,
{
let data: Vec<Complex<T>> = input.iter().map(num_complex::Complex::conj).collect();
Array::from_vec(input.dim().clone(), data)
}
pub fn conjugate<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<Complex<T>, D>>
where
T: Element + Float,
Complex<T>: Element,
D: Dimension,
{
conj(input)
}
pub fn angle<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<T, D>>
where
T: Element + Float,
Complex<T>: Element,
D: Dimension,
{
let data: Vec<T> = input.iter().map(|c| c.im.atan2(c.re)).collect();
Array::from_vec(input.dim().clone(), data)
}
pub fn abs<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<T, D>>
where
T: Element + Float,
Complex<T>: Element,
D: Dimension,
{
let data: Vec<T> = input.iter().map(|c| c.re.hypot(c.im)).collect();
Array::from_vec(input.dim().clone(), data)
}
pub fn iscomplex<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<bool, D>>
where
T: Element + Float,
Complex<T>: Element,
D: Dimension,
{
let zero = <T as num_traits::Zero>::zero();
let data: Vec<bool> = input.iter().map(|c| c.im != zero).collect();
Array::from_vec(input.dim().clone(), data)
}
pub fn iscomplex_real<T, D>(input: &Array<T, D>) -> FerrayResult<Array<bool, D>>
where
T: Element,
D: Dimension,
{
let data = vec![false; input.size()];
Array::from_vec(input.dim().clone(), data)
}
pub fn isreal<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<bool, D>>
where
T: Element + Float,
Complex<T>: Element,
D: Dimension,
{
let zero = <T as num_traits::Zero>::zero();
let data: Vec<bool> = input.iter().map(|c| c.im == zero).collect();
Array::from_vec(input.dim().clone(), data)
}
pub fn isreal_real<T, D>(input: &Array<T, D>) -> FerrayResult<Array<bool, D>>
where
T: Element,
D: Dimension,
{
let data = vec![true; input.size()];
Array::from_vec(input.dim().clone(), data)
}
pub fn iscomplexobj<T, D>(_: &Array<T, D>) -> bool
where
T: Element,
D: Dimension,
{
T::dtype().is_complex()
}
pub fn isrealobj<T, D>(a: &Array<T, D>) -> bool
where
T: Element,
D: Dimension,
{
!iscomplexobj(a)
}
pub fn isscalar<T, D>(a: &Array<T, D>) -> bool
where
T: Element,
D: Dimension,
{
a.shape().is_empty()
}
macro_rules! complex_unary {
($fn_name:ident, $method:ident, $doc:expr) => {
#[doc = $doc]
pub fn $fn_name<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<Complex<T>, D>>
where
T: Element + Float,
Complex<T>: Element,
D: Dimension,
{
let data: Vec<Complex<T>> = input.iter().map(|z| z.$method()).collect();
Array::from_vec(input.dim().clone(), data)
}
};
}
fn fix_real_axis_cut<T>(
z: Complex<T>,
follow: bool,
f: impl Fn(Complex<T>) -> Complex<T>,
) -> Complex<T>
where
T: Element + Float,
{
let zero = <T as num_traits::Zero>::zero();
if z.im == zero {
let base = f(z);
let want_neg = z.im.is_sign_negative() == follow;
if base.im.is_sign_negative() == want_neg {
base
} else {
f(z.conj()).conj()
}
} else {
f(z)
}
}
fn fix_imag_axis_cut<T>(z: Complex<T>, f: impl Fn(Complex<T>) -> Complex<T>) -> Complex<T>
where
T: Element + Float,
{
let zero = <T as num_traits::Zero>::zero();
if z.re == zero {
let base = f(z);
let want_neg = z.re.is_sign_negative();
if base.re.is_sign_negative() == want_neg {
base
} else {
f(z.conj()).conj()
}
} else {
f(z)
}
}
complex_unary!(
sin_complex,
sin,
"Element-wise complex sin: sin(z) = sin(z.re)cosh(z.im) + i cos(z.re)sinh(z.im)."
);
complex_unary!(cos_complex, cos, "Element-wise complex cos.");
complex_unary!(tan_complex, tan, "Element-wise complex tan.");
complex_unary!(sinh_complex, sinh, "Element-wise complex sinh.");
complex_unary!(cosh_complex, cosh, "Element-wise complex cosh.");
complex_unary!(tanh_complex, tanh, "Element-wise complex tanh.");
pub fn asin_complex<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<Complex<T>, D>>
where
T: Element + Float,
Complex<T>: Element,
D: Dimension,
{
let data: Vec<Complex<T>> = input
.iter()
.map(|z| fix_real_axis_cut(*z, true, |w| w.asin()))
.collect();
Array::from_vec(input.dim().clone(), data)
}
pub fn acos_complex<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<Complex<T>, D>>
where
T: Element + Float,
Complex<T>: Element,
D: Dimension,
{
let data: Vec<Complex<T>> = input
.iter()
.map(|z| fix_real_axis_cut(*z, false, |w| w.acos()))
.collect();
Array::from_vec(input.dim().clone(), data)
}
pub fn atan_complex<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<Complex<T>, D>>
where
T: Element + Float,
Complex<T>: Element,
D: Dimension,
{
let data: Vec<Complex<T>> = input
.iter()
.map(|z| fix_imag_axis_cut(*z, |w| w.atan()))
.collect();
Array::from_vec(input.dim().clone(), data)
}
pub fn asinh_complex<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<Complex<T>, D>>
where
T: Element + Float,
Complex<T>: Element,
D: Dimension,
{
let data: Vec<Complex<T>> = input
.iter()
.map(|z| fix_imag_axis_cut(*z, |w| w.asinh()))
.collect();
Array::from_vec(input.dim().clone(), data)
}
pub fn acosh_complex<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<Complex<T>, D>>
where
T: Element + Float,
Complex<T>: Element,
D: Dimension,
{
let data: Vec<Complex<T>> = input
.iter()
.map(|z| fix_real_axis_cut(*z, true, |w| w.acosh()))
.collect();
Array::from_vec(input.dim().clone(), data)
}
pub fn atanh_complex<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<Complex<T>, D>>
where
T: Element + Float,
Complex<T>: Element,
D: Dimension,
{
let data: Vec<Complex<T>> = input
.iter()
.map(|z| fix_real_axis_cut(*z, true, |w| w.atanh()))
.collect();
Array::from_vec(input.dim().clone(), data)
}
complex_unary!(
exp_complex,
exp,
"Element-wise complex exp: exp(z) = exp(z.re) (cos(z.im) + i sin(z.im))."
);
complex_unary!(
ln_complex,
ln,
"Element-wise complex natural log: log(z) = log|z| + i arg(z), branch cut along negative real axis."
);
complex_unary!(
sqrt_complex,
sqrt,
"Element-wise complex sqrt; principal branch (Re(sqrt(z)) >= 0)."
);
pub fn log2_complex<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<Complex<T>, D>>
where
T: Element + Float,
Complex<T>: Element,
D: Dimension,
{
let one = <T as num_traits::One>::one();
let ln2: T = <T as num_traits::NumCast>::from(core::f64::consts::LN_2).unwrap();
let inv_ln2 = one / ln2;
let data: Vec<Complex<T>> = input
.iter()
.map(|z| {
let l = z.ln();
Complex::new(l.re * inv_ln2, l.im * inv_ln2)
})
.collect();
Array::from_vec(input.dim().clone(), data)
}
pub fn log10_complex<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<Complex<T>, D>>
where
T: Element + Float,
Complex<T>: Element,
D: Dimension,
{
let one = <T as num_traits::One>::one();
let ln10: T = <T as num_traits::NumCast>::from(core::f64::consts::LN_10).unwrap();
let inv_ln10 = one / ln10;
let data: Vec<Complex<T>> = input
.iter()
.map(|z| {
let l = z.ln();
Complex::new(l.re * inv_ln10, l.im * inv_ln10)
})
.collect();
Array::from_vec(input.dim().clone(), data)
}
pub fn expm1_complex<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<Complex<T>, D>>
where
T: Element + Float,
Complex<T>: Element,
D: Dimension,
{
let one = <T as num_traits::One>::one();
let two = one + one;
let half: T = <T as num_traits::NumCast>::from(0.5_f64).unwrap();
let data: Vec<Complex<T>> = input
.iter()
.map(|z| {
let er_m1 = z.re.exp_m1();
let er = er_m1 + one;
let cos_im = z.im.cos();
let sin_im = z.im.sin();
let s = (z.im * half).sin();
let cos_im_m1 = -two * s * s;
Complex::new(er_m1 * cos_im + cos_im_m1, er * sin_im)
})
.collect();
Array::from_vec(input.dim().clone(), data)
}
pub fn log1p_complex<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<Complex<T>, D>>
where
T: Element + Float,
Complex<T>: Element,
D: Dimension,
{
let one = <T as num_traits::One>::one();
let zero = <T as num_traits::Zero>::zero();
let two = one + one;
let half: T = <T as num_traits::NumCast>::from(0.5_f64).unwrap();
let small: T = <T as num_traits::NumCast>::from(0.5_f64).unwrap();
let data: Vec<Complex<T>> = input
.iter()
.map(|z| {
if z.re.abs() < small && z.im.abs() < small {
let u = z.re * (two + z.re) + z.im * z.im;
let half_log = half * u.ln_1p();
let arg = z.im.atan2(one + z.re);
Complex::new(half_log, arg)
} else {
(Complex::new(one, zero) + *z).ln()
}
})
.collect();
Array::from_vec(input.dim().clone(), data)
}
pub fn power_complex<T, D>(
base: &Array<Complex<T>, D>,
exponent: Complex<T>,
) -> FerrayResult<Array<Complex<T>, D>>
where
T: Element + Float,
Complex<T>: Element,
D: Dimension,
{
let data: Vec<Complex<T>> = base.iter().map(|z| z.powc(exponent)).collect();
Array::from_vec(base.dim().clone(), data)
}
#[cfg(test)]
mod tests {
use super::*;
use ferray_core::dimension::Ix1;
use num_complex::Complex64;
fn arr1_c64(data: Vec<Complex64>) -> Array<Complex64, Ix1> {
let n = data.len();
Array::from_vec(Ix1::new([n]), data).unwrap()
}
#[test]
fn test_real() {
let a = arr1_c64(vec![Complex64::new(1.0, 2.0), Complex64::new(3.0, 4.0)]);
let r = real(&a).unwrap();
assert_eq!(r.as_slice().unwrap(), &[1.0, 3.0]);
}
#[test]
fn test_imag() {
let a = arr1_c64(vec![Complex64::new(1.0, 2.0), Complex64::new(3.0, 4.0)]);
let r = imag(&a).unwrap();
assert_eq!(r.as_slice().unwrap(), &[2.0, 4.0]);
}
#[test]
fn test_conj() {
let a = arr1_c64(vec![Complex64::new(1.0, 2.0), Complex64::new(3.0, -4.0)]);
let r = conj(&a).unwrap();
let s = r.as_slice().unwrap();
assert_eq!(s[0], Complex64::new(1.0, -2.0));
assert_eq!(s[1], Complex64::new(3.0, 4.0));
}
#[test]
fn test_conjugate_alias() {
let a = arr1_c64(vec![Complex64::new(1.0, 2.0)]);
let r = conjugate(&a).unwrap();
assert_eq!(r.as_slice().unwrap()[0], Complex64::new(1.0, -2.0));
}
#[test]
fn test_angle() {
let a = arr1_c64(vec![
Complex64::new(1.0, 0.0),
Complex64::new(0.0, 1.0),
Complex64::new(-1.0, 0.0),
]);
let r = angle(&a).unwrap();
let s = r.as_slice().unwrap();
assert!((s[0] - 0.0).abs() < 1e-12);
assert!((s[1] - std::f64::consts::FRAC_PI_2).abs() < 1e-12);
assert!((s[2] - std::f64::consts::PI).abs() < 1e-12);
}
#[test]
fn test_abs() {
let a = arr1_c64(vec![Complex64::new(3.0, 4.0), Complex64::new(0.0, 1.0)]);
let r = abs(&a).unwrap();
let s = r.as_slice().unwrap();
assert!((s[0] - 5.0).abs() < 1e-12);
assert!((s[1] - 1.0).abs() < 1e-12);
}
#[test]
fn test_iscomplex_complex_input() {
let a = arr1_c64(vec![
Complex64::new(1.0, 0.0),
Complex64::new(2.0, 1.5),
Complex64::new(0.0, 0.0),
]);
let r = iscomplex(&a).unwrap();
assert_eq!(r.as_slice().unwrap(), &[false, true, false]);
}
#[test]
fn test_isreal_complex_input() {
let a = arr1_c64(vec![Complex64::new(1.0, 0.0), Complex64::new(2.0, 1.5)]);
let r = isreal(&a).unwrap();
assert_eq!(r.as_slice().unwrap(), &[true, false]);
}
#[test]
fn test_iscomplex_real_input() {
let a: Array<f64, Ix1> = Array::from_vec(Ix1::new([3]), vec![1.0, 2.0, 3.0]).unwrap();
let r = iscomplex_real(&a).unwrap();
assert_eq!(r.as_slice().unwrap(), &[false, false, false]);
}
#[test]
fn test_isreal_real_input() {
let a: Array<f64, Ix1> = Array::from_vec(Ix1::new([3]), vec![1.0, 2.0, 3.0]).unwrap();
let r = isreal_real(&a).unwrap();
assert_eq!(r.as_slice().unwrap(), &[true, true, true]);
}
#[test]
fn test_iscomplexobj_isrealobj() {
let c = arr1_c64(vec![Complex64::new(1.0, 2.0)]);
assert!(iscomplexobj(&c));
assert!(!isrealobj(&c));
let r: Array<f64, Ix1> = Array::from_vec(Ix1::new([2]), vec![1.0, 2.0]).unwrap();
assert!(!iscomplexobj(&r));
assert!(isrealobj(&r));
}
fn approx_eq_c(a: Complex64, b: Complex64, eps: f64) -> bool {
(a.re - b.re).abs() <= eps && (a.im - b.im).abs() <= eps
}
#[test]
fn test_sin_complex_matches_identity() {
let arr = arr1_c64(vec![Complex64::new(0.0, 1.0), Complex64::new(1.0, 1.0)]);
let out = sin_complex(&arr).unwrap();
let v0 = out.iter().next().copied().unwrap();
assert!(approx_eq_c(
v0,
Complex64::new(0.0, 1.1752011936438014),
1e-10
));
}
#[test]
fn test_cos_complex_matches_identity() {
let arr = arr1_c64(vec![Complex64::new(0.0, 1.0)]);
let out = cos_complex(&arr).unwrap();
let v0 = out.iter().next().copied().unwrap();
assert!(approx_eq_c(
v0,
Complex64::new(1.5430806348152437, 0.0),
1e-10
));
}
#[test]
fn test_exp_complex_eulers_identity() {
let arr = arr1_c64(vec![Complex64::new(0.0, std::f64::consts::PI)]);
let out = exp_complex(&arr).unwrap();
let v0 = out.iter().next().copied().unwrap();
assert!(approx_eq_c(v0, Complex64::new(-1.0, 0.0), 1e-12));
}
#[test]
fn test_ln_complex_inverse_of_exp() {
let z = Complex64::new(0.7, 1.3);
let arr = arr1_c64(vec![z.exp()]);
let out = ln_complex(&arr).unwrap();
let v = out.iter().next().copied().unwrap();
assert!(approx_eq_c(v, z, 1e-12));
}
#[test]
fn test_sqrt_complex_principal_branch() {
let arr = arr1_c64(vec![Complex64::new(0.0, 1.0)]);
let out = sqrt_complex(&arr).unwrap();
let v = out.iter().next().copied().unwrap();
let inv_sqrt2 = 1.0_f64 / 2.0_f64.sqrt();
assert!(approx_eq_c(v, Complex64::new(inv_sqrt2, inv_sqrt2), 1e-12));
}
#[test]
fn test_log2_complex() {
let arr = arr1_c64(vec![Complex64::new(2.0, 0.0)]);
let out = log2_complex(&arr).unwrap();
let v = out.iter().next().copied().unwrap();
assert!(approx_eq_c(v, Complex64::new(1.0, 0.0), 1e-12));
}
#[test]
fn test_log10_complex() {
let arr = arr1_c64(vec![Complex64::new(100.0, 0.0)]);
let out = log10_complex(&arr).unwrap();
let v = out.iter().next().copied().unwrap();
assert!(approx_eq_c(v, Complex64::new(2.0, 0.0), 1e-12));
}
#[test]
fn test_expm1_complex_small_arg_precision() {
let z = Complex64::new(1e-12, 1e-12);
let arr = arr1_c64(vec![z]);
let out = expm1_complex(&arr).unwrap();
let v = out.iter().next().copied().unwrap();
let rel_err = (v - z).norm() / z.norm();
assert!(
rel_err < 1e-4,
"expm1 small-arg rel_err = {rel_err:e}, v = {v:?}"
);
}
#[test]
fn test_log1p_complex_small_arg_precision() {
let z = Complex64::new(1e-10, 1e-10);
let arr = arr1_c64(vec![z]);
let out = log1p_complex(&arr).unwrap();
let v = out.iter().next().copied().unwrap();
let rel_err = (v - z).norm() / z.norm();
assert!(
rel_err < 1e-4,
"log1p small-arg rel_err = {rel_err:e}, v = {v:?}"
);
}
#[test]
fn test_expm1_complex_moderate_arg_matches_naive() {
let z = Complex64::new(0.5, 0.3);
let arr = arr1_c64(vec![z]);
let out = expm1_complex(&arr).unwrap();
let v = out.iter().next().copied().unwrap();
let expected = z.exp() - Complex64::new(1.0, 0.0);
assert!(approx_eq_c(v, expected, 1e-12));
}
#[test]
fn test_log1p_complex_moderate_arg_matches_naive() {
let z = Complex64::new(0.4, 0.2);
let arr = arr1_c64(vec![z]);
let out = log1p_complex(&arr).unwrap();
let v = out.iter().next().copied().unwrap();
let expected = (Complex64::new(1.0, 0.0) + z).ln();
assert!(approx_eq_c(v, expected, 1e-12));
}
#[test]
fn test_power_complex_integer_exponent() {
let arr = arr1_c64(vec![Complex64::new(1.0, 1.0)]);
let out = power_complex(&arr, Complex64::new(2.0, 0.0)).unwrap();
let v = out.iter().next().copied().unwrap();
assert!(approx_eq_c(v, Complex64::new(0.0, 2.0), 1e-12));
}
#[test]
fn test_tanh_complex_round_trip() {
let z = Complex64::new(0.5, 0.3);
let arr = arr1_c64(vec![z]);
let t = tanh_complex(&arr).unwrap();
let r = atanh_complex(&t).unwrap();
let v = r.iter().next().copied().unwrap();
assert!(approx_eq_c(v, z, 1e-12));
}
const MAG_S: f64 = 1.3169578969248166; const HALF_PI: f64 = std::f64::consts::FRAC_PI_2;
const PI: f64 = std::f64::consts::PI;
type ComplexUnaryFn = fn(&Array<Complex64, Ix1>) -> FerrayResult<Array<Complex64, Ix1>>;
fn call1(z: Complex64, f: ComplexUnaryFn) -> Complex64 {
match f(&arr1_c64(vec![z]))
.ok()
.and_then(|a| a.iter().next().copied())
{
Some(v) => v,
None => Complex64::new(f64::NAN, f64::NAN),
}
}
#[test]
fn test_arcsin_branch_cut_signed_zero() {
assert!(approx_eq_c(
call1(Complex64::new(2.0, 0.0), asin_complex),
Complex64::new(HALF_PI, MAG_S),
1e-12
));
assert!(approx_eq_c(
call1(Complex64::new(2.0, -0.0), asin_complex),
Complex64::new(HALF_PI, -MAG_S),
1e-12
));
assert!(approx_eq_c(
call1(Complex64::new(-2.0, 0.0), asin_complex),
Complex64::new(-HALF_PI, MAG_S),
1e-12
));
assert!(approx_eq_c(
call1(Complex64::new(-2.0, -0.0), asin_complex),
Complex64::new(-HALF_PI, -MAG_S),
1e-12
));
}
#[test]
fn test_arccos_branch_cut_signed_zero() {
assert!(approx_eq_c(
call1(Complex64::new(2.0, 0.0), acos_complex),
Complex64::new(0.0, -MAG_S),
1e-12
));
assert!(approx_eq_c(
call1(Complex64::new(2.0, -0.0), acos_complex),
Complex64::new(0.0, MAG_S),
1e-12
));
assert!(approx_eq_c(
call1(Complex64::new(-2.0, 0.0), acos_complex),
Complex64::new(PI, -MAG_S),
1e-12
));
assert!(approx_eq_c(
call1(Complex64::new(-2.0, -0.0), acos_complex),
Complex64::new(PI, MAG_S),
1e-12
));
}
#[test]
fn test_arctanh_branch_cut_signed_zero() {
let re_m = 0.5493061443340549;
assert!(approx_eq_c(
call1(Complex64::new(2.0, 0.0), atanh_complex),
Complex64::new(re_m, HALF_PI),
1e-12
));
assert!(approx_eq_c(
call1(Complex64::new(2.0, -0.0), atanh_complex),
Complex64::new(re_m, -HALF_PI),
1e-12
));
assert!(approx_eq_c(
call1(Complex64::new(-2.0, 0.0), atanh_complex),
Complex64::new(-re_m, HALF_PI),
1e-12
));
assert!(approx_eq_c(
call1(Complex64::new(-2.0, -0.0), atanh_complex),
Complex64::new(-re_m, -HALF_PI),
1e-12
));
}
#[test]
fn test_arcsinh_branch_cut_signed_zero() {
assert!(approx_eq_c(
call1(Complex64::new(0.0, 2.0), asinh_complex),
Complex64::new(MAG_S, HALF_PI),
1e-12
));
assert!(approx_eq_c(
call1(Complex64::new(-0.0, 2.0), asinh_complex),
Complex64::new(-MAG_S, HALF_PI),
1e-12
));
assert!(approx_eq_c(
call1(Complex64::new(0.0, -2.0), asinh_complex),
Complex64::new(MAG_S, -HALF_PI),
1e-12
));
assert!(approx_eq_c(
call1(Complex64::new(-0.0, -2.0), asinh_complex),
Complex64::new(-MAG_S, -HALF_PI),
1e-12
));
}
#[test]
fn test_arctan_branch_cut_signed_zero() {
let im_m = 0.5493061443340549;
assert!(approx_eq_c(
call1(Complex64::new(0.0, 2.0), atan_complex),
Complex64::new(HALF_PI, im_m),
1e-12
));
assert!(approx_eq_c(
call1(Complex64::new(-0.0, 2.0), atan_complex),
Complex64::new(-HALF_PI, im_m),
1e-12
));
}
#[test]
fn test_arccosh_branch_cut_signed_zero() {
assert!(approx_eq_c(
call1(Complex64::new(-2.0, 0.0), acosh_complex),
Complex64::new(MAG_S, PI),
1e-12
));
assert!(approx_eq_c(
call1(Complex64::new(-2.0, -0.0), acosh_complex),
Complex64::new(MAG_S, -PI),
1e-12
));
let m = 1.0471975511965976;
assert!(approx_eq_c(
call1(Complex64::new(0.5, 0.0), acosh_complex),
Complex64::new(0.0, m),
1e-12
));
assert!(approx_eq_c(
call1(Complex64::new(0.5, -0.0), acosh_complex),
Complex64::new(0.0, -m),
1e-12
));
}
#[test]
fn test_arc_interior_points_unchanged() {
let z = Complex64::new(1.0, 1.0);
assert_eq!(call1(z, asin_complex), z.asin());
assert_eq!(call1(z, acos_complex), z.acos());
assert_eq!(call1(z, atanh_complex), z.atanh());
assert_eq!(call1(z, asinh_complex), z.asinh());
assert_eq!(call1(z, atan_complex), z.atan());
assert_eq!(call1(z, acosh_complex), z.acosh());
}
#[test]
fn test_isscalar_zero_d() {
use ferray_core::dimension::IxDyn;
let scalar: Array<f64, IxDyn> = Array::from_vec(IxDyn::new(&[]), vec![2.5]).unwrap();
assert!(isscalar(&scalar));
let vec: Array<f64, Ix1> = Array::from_vec(Ix1::new([3]), vec![1.0, 2.0, 3.0]).unwrap();
assert!(!isscalar(&vec));
}
}