1use ferray_core::Array;
36use ferray_core::dimension::Dimension;
37use ferray_core::dtype::Element;
38use ferray_core::error::FerrayResult;
39use num_complex::Complex;
40use num_traits::Float;
41
42pub fn real<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<T, D>>
46where
47 T: Element + Float,
48 Complex<T>: Element,
49 D: Dimension,
50{
51 let data: Vec<T> = input.iter().map(|c| c.re).collect();
52 Array::from_vec(input.dim().clone(), data)
53}
54
55pub fn imag<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<T, D>>
57where
58 T: Element + Float,
59 Complex<T>: Element,
60 D: Dimension,
61{
62 let data: Vec<T> = input.iter().map(|c| c.im).collect();
63 Array::from_vec(input.dim().clone(), data)
64}
65
66pub fn conj<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<Complex<T>, D>>
68where
69 T: Element + Float,
70 Complex<T>: Element,
71 D: Dimension,
72{
73 let data: Vec<Complex<T>> = input.iter().map(num_complex::Complex::conj).collect();
74 Array::from_vec(input.dim().clone(), data)
75}
76
77pub fn conjugate<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<Complex<T>, D>>
79where
80 T: Element + Float,
81 Complex<T>: Element,
82 D: Dimension,
83{
84 conj(input)
85}
86
87pub fn angle<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<T, D>>
91where
92 T: Element + Float,
93 Complex<T>: Element,
94 D: Dimension,
95{
96 let data: Vec<T> = input.iter().map(|c| c.im.atan2(c.re)).collect();
97 Array::from_vec(input.dim().clone(), data)
98}
99
100pub fn abs<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<T, D>>
104where
105 T: Element + Float,
106 Complex<T>: Element,
107 D: Dimension,
108{
109 let data: Vec<T> = input.iter().map(|c| c.re.hypot(c.im)).collect();
115 Array::from_vec(input.dim().clone(), data)
116}
117
118pub fn iscomplex<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<bool, D>>
128where
129 T: Element + Float,
130 Complex<T>: Element,
131 D: Dimension,
132{
133 let zero = <T as num_traits::Zero>::zero();
134 let data: Vec<bool> = input.iter().map(|c| c.im != zero).collect();
135 Array::from_vec(input.dim().clone(), data)
136}
137
138pub fn iscomplex_real<T, D>(input: &Array<T, D>) -> FerrayResult<Array<bool, D>>
144where
145 T: Element,
146 D: Dimension,
147{
148 let data = vec![false; input.size()];
149 Array::from_vec(input.dim().clone(), data)
150}
151
152pub fn isreal<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<bool, D>>
157where
158 T: Element + Float,
159 Complex<T>: Element,
160 D: Dimension,
161{
162 let zero = <T as num_traits::Zero>::zero();
163 let data: Vec<bool> = input.iter().map(|c| c.im == zero).collect();
164 Array::from_vec(input.dim().clone(), data)
165}
166
167pub fn isreal_real<T, D>(input: &Array<T, D>) -> FerrayResult<Array<bool, D>>
169where
170 T: Element,
171 D: Dimension,
172{
173 let data = vec![true; input.size()];
174 Array::from_vec(input.dim().clone(), data)
175}
176
177pub fn iscomplexobj<T, D>(_: &Array<T, D>) -> bool
182where
183 T: Element,
184 D: Dimension,
185{
186 T::dtype().is_complex()
187}
188
189pub fn isrealobj<T, D>(a: &Array<T, D>) -> bool
193where
194 T: Element,
195 D: Dimension,
196{
197 !iscomplexobj(a)
198}
199
200pub fn isscalar<T, D>(a: &Array<T, D>) -> bool
206where
207 T: Element,
208 D: Dimension,
209{
210 a.shape().is_empty()
211}
212
213macro_rules! complex_unary {
226 ($fn_name:ident, $method:ident, $doc:expr) => {
227 #[doc = $doc]
228 pub fn $fn_name<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<Complex<T>, D>>
229 where
230 T: Element + Float,
231 Complex<T>: Element,
232 D: Dimension,
233 {
234 let data: Vec<Complex<T>> = input.iter().map(|z| z.$method()).collect();
235 Array::from_vec(input.dim().clone(), data)
236 }
237 };
238}
239
240fn fix_real_axis_cut<T>(
264 z: Complex<T>,
265 follow: bool,
266 f: impl Fn(Complex<T>) -> Complex<T>,
267) -> Complex<T>
268where
269 T: Element + Float,
270{
271 let zero = <T as num_traits::Zero>::zero();
272 if z.im == zero {
273 let base = f(z);
274 let want_neg = z.im.is_sign_negative() == follow;
275 if base.im.is_sign_negative() == want_neg {
276 base
277 } else {
278 f(z.conj()).conj()
279 }
280 } else {
281 f(z)
282 }
283}
284
285fn fix_imag_axis_cut<T>(z: Complex<T>, f: impl Fn(Complex<T>) -> Complex<T>) -> Complex<T>
288where
289 T: Element + Float,
290{
291 let zero = <T as num_traits::Zero>::zero();
292 if z.re == zero {
293 let base = f(z);
294 let want_neg = z.re.is_sign_negative();
295 if base.re.is_sign_negative() == want_neg {
296 base
297 } else {
298 f(z.conj()).conj()
299 }
300 } else {
301 f(z)
302 }
303}
304
305complex_unary!(
306 sin_complex,
307 sin,
308 "Element-wise complex sin: sin(z) = sin(z.re)cosh(z.im) + i cos(z.re)sinh(z.im)."
309);
310complex_unary!(cos_complex, cos, "Element-wise complex cos.");
311complex_unary!(tan_complex, tan, "Element-wise complex tan.");
312complex_unary!(sinh_complex, sinh, "Element-wise complex sinh.");
313complex_unary!(cosh_complex, cosh, "Element-wise complex cosh.");
314complex_unary!(tanh_complex, tanh, "Element-wise complex tanh.");
315
316pub fn asin_complex<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<Complex<T>, D>>
319where
320 T: Element + Float,
321 Complex<T>: Element,
322 D: Dimension,
323{
324 let data: Vec<Complex<T>> = input
325 .iter()
326 .map(|z| fix_real_axis_cut(*z, true, |w| w.asin()))
327 .collect();
328 Array::from_vec(input.dim().clone(), data)
329}
330
331pub fn acos_complex<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<Complex<T>, D>>
335where
336 T: Element + Float,
337 Complex<T>: Element,
338 D: Dimension,
339{
340 let data: Vec<Complex<T>> = input
341 .iter()
342 .map(|z| fix_real_axis_cut(*z, false, |w| w.acos()))
343 .collect();
344 Array::from_vec(input.dim().clone(), data)
345}
346
347pub fn atan_complex<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<Complex<T>, D>>
350where
351 T: Element + Float,
352 Complex<T>: Element,
353 D: Dimension,
354{
355 let data: Vec<Complex<T>> = input
356 .iter()
357 .map(|z| fix_imag_axis_cut(*z, |w| w.atan()))
358 .collect();
359 Array::from_vec(input.dim().clone(), data)
360}
361
362pub fn asinh_complex<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<Complex<T>, D>>
365where
366 T: Element + Float,
367 Complex<T>: Element,
368 D: Dimension,
369{
370 let data: Vec<Complex<T>> = input
371 .iter()
372 .map(|z| fix_imag_axis_cut(*z, |w| w.asinh()))
373 .collect();
374 Array::from_vec(input.dim().clone(), data)
375}
376
377pub fn acosh_complex<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<Complex<T>, D>>
380where
381 T: Element + Float,
382 Complex<T>: Element,
383 D: Dimension,
384{
385 let data: Vec<Complex<T>> = input
386 .iter()
387 .map(|z| fix_real_axis_cut(*z, true, |w| w.acosh()))
388 .collect();
389 Array::from_vec(input.dim().clone(), data)
390}
391
392pub fn atanh_complex<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<Complex<T>, D>>
395where
396 T: Element + Float,
397 Complex<T>: Element,
398 D: Dimension,
399{
400 let data: Vec<Complex<T>> = input
401 .iter()
402 .map(|z| fix_real_axis_cut(*z, true, |w| w.atanh()))
403 .collect();
404 Array::from_vec(input.dim().clone(), data)
405}
406complex_unary!(
407 exp_complex,
408 exp,
409 "Element-wise complex exp: exp(z) = exp(z.re) (cos(z.im) + i sin(z.im))."
410);
411complex_unary!(
412 ln_complex,
413 ln,
414 "Element-wise complex natural log: log(z) = log|z| + i arg(z), branch cut along negative real axis."
415);
416complex_unary!(
417 sqrt_complex,
418 sqrt,
419 "Element-wise complex sqrt; principal branch (Re(sqrt(z)) >= 0)."
420);
421
422pub fn log2_complex<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<Complex<T>, D>>
424where
425 T: Element + Float,
426 Complex<T>: Element,
427 D: Dimension,
428{
429 let one = <T as num_traits::One>::one();
430 let ln2: T = <T as num_traits::NumCast>::from(core::f64::consts::LN_2).unwrap();
431 let inv_ln2 = one / ln2;
432 let data: Vec<Complex<T>> = input
433 .iter()
434 .map(|z| {
435 let l = z.ln();
436 Complex::new(l.re * inv_ln2, l.im * inv_ln2)
437 })
438 .collect();
439 Array::from_vec(input.dim().clone(), data)
440}
441
442pub fn log10_complex<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<Complex<T>, D>>
444where
445 T: Element + Float,
446 Complex<T>: Element,
447 D: Dimension,
448{
449 let one = <T as num_traits::One>::one();
450 let ln10: T = <T as num_traits::NumCast>::from(core::f64::consts::LN_10).unwrap();
451 let inv_ln10 = one / ln10;
452 let data: Vec<Complex<T>> = input
453 .iter()
454 .map(|z| {
455 let l = z.ln();
456 Complex::new(l.re * inv_ln10, l.im * inv_ln10)
457 })
458 .collect();
459 Array::from_vec(input.dim().clone(), data)
460}
461
462pub fn expm1_complex<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<Complex<T>, D>>
467where
468 T: Element + Float,
469 Complex<T>: Element,
470 D: Dimension,
471{
472 let one = <T as num_traits::One>::one();
473 let two = one + one;
474 let half: T = <T as num_traits::NumCast>::from(0.5_f64).unwrap();
475 let data: Vec<Complex<T>> = input
476 .iter()
477 .map(|z| {
478 let er_m1 = z.re.exp_m1();
481 let er = er_m1 + one;
482 let cos_im = z.im.cos();
483 let sin_im = z.im.sin();
484 let s = (z.im * half).sin();
486 let cos_im_m1 = -two * s * s;
487 Complex::new(er_m1 * cos_im + cos_im_m1, er * sin_im)
488 })
489 .collect();
490 Array::from_vec(input.dim().clone(), data)
491}
492
493pub fn log1p_complex<T, D>(input: &Array<Complex<T>, D>) -> FerrayResult<Array<Complex<T>, D>>
499where
500 T: Element + Float,
501 Complex<T>: Element,
502 D: Dimension,
503{
504 let one = <T as num_traits::One>::one();
505 let zero = <T as num_traits::Zero>::zero();
506 let two = one + one;
507 let half: T = <T as num_traits::NumCast>::from(0.5_f64).unwrap();
508 let small: T = <T as num_traits::NumCast>::from(0.5_f64).unwrap();
509 let data: Vec<Complex<T>> = input
510 .iter()
511 .map(|z| {
512 if z.re.abs() < small && z.im.abs() < small {
513 let u = z.re * (two + z.re) + z.im * z.im;
516 let half_log = half * u.ln_1p();
517 let arg = z.im.atan2(one + z.re);
518 Complex::new(half_log, arg)
519 } else {
520 (Complex::new(one, zero) + *z).ln()
521 }
522 })
523 .collect();
524 Array::from_vec(input.dim().clone(), data)
525}
526
527pub fn power_complex<T, D>(
530 base: &Array<Complex<T>, D>,
531 exponent: Complex<T>,
532) -> FerrayResult<Array<Complex<T>, D>>
533where
534 T: Element + Float,
535 Complex<T>: Element,
536 D: Dimension,
537{
538 let data: Vec<Complex<T>> = base.iter().map(|z| z.powc(exponent)).collect();
539 Array::from_vec(base.dim().clone(), data)
540}
541
542#[cfg(test)]
543mod tests {
544 use super::*;
545 use ferray_core::dimension::Ix1;
546 use num_complex::Complex64;
547
548 fn arr1_c64(data: Vec<Complex64>) -> Array<Complex64, Ix1> {
549 let n = data.len();
550 Array::from_vec(Ix1::new([n]), data).unwrap()
551 }
552
553 #[test]
554 fn test_real() {
555 let a = arr1_c64(vec![Complex64::new(1.0, 2.0), Complex64::new(3.0, 4.0)]);
556 let r = real(&a).unwrap();
557 assert_eq!(r.as_slice().unwrap(), &[1.0, 3.0]);
558 }
559
560 #[test]
561 fn test_imag() {
562 let a = arr1_c64(vec![Complex64::new(1.0, 2.0), Complex64::new(3.0, 4.0)]);
563 let r = imag(&a).unwrap();
564 assert_eq!(r.as_slice().unwrap(), &[2.0, 4.0]);
565 }
566
567 #[test]
568 fn test_conj() {
569 let a = arr1_c64(vec![Complex64::new(1.0, 2.0), Complex64::new(3.0, -4.0)]);
570 let r = conj(&a).unwrap();
571 let s = r.as_slice().unwrap();
572 assert_eq!(s[0], Complex64::new(1.0, -2.0));
573 assert_eq!(s[1], Complex64::new(3.0, 4.0));
574 }
575
576 #[test]
577 fn test_conjugate_alias() {
578 let a = arr1_c64(vec![Complex64::new(1.0, 2.0)]);
579 let r = conjugate(&a).unwrap();
580 assert_eq!(r.as_slice().unwrap()[0], Complex64::new(1.0, -2.0));
581 }
582
583 #[test]
584 fn test_angle() {
585 let a = arr1_c64(vec![
586 Complex64::new(1.0, 0.0),
587 Complex64::new(0.0, 1.0),
588 Complex64::new(-1.0, 0.0),
589 ]);
590 let r = angle(&a).unwrap();
591 let s = r.as_slice().unwrap();
592 assert!((s[0] - 0.0).abs() < 1e-12);
593 assert!((s[1] - std::f64::consts::FRAC_PI_2).abs() < 1e-12);
594 assert!((s[2] - std::f64::consts::PI).abs() < 1e-12);
595 }
596
597 #[test]
598 fn test_abs() {
599 let a = arr1_c64(vec![Complex64::new(3.0, 4.0), Complex64::new(0.0, 1.0)]);
600 let r = abs(&a).unwrap();
601 let s = r.as_slice().unwrap();
602 assert!((s[0] - 5.0).abs() < 1e-12);
603 assert!((s[1] - 1.0).abs() < 1e-12);
604 }
605
606 #[test]
607 fn test_iscomplex_complex_input() {
608 let a = arr1_c64(vec![
609 Complex64::new(1.0, 0.0),
610 Complex64::new(2.0, 1.5),
611 Complex64::new(0.0, 0.0),
612 ]);
613 let r = iscomplex(&a).unwrap();
614 assert_eq!(r.as_slice().unwrap(), &[false, true, false]);
615 }
616
617 #[test]
618 fn test_isreal_complex_input() {
619 let a = arr1_c64(vec![Complex64::new(1.0, 0.0), Complex64::new(2.0, 1.5)]);
620 let r = isreal(&a).unwrap();
621 assert_eq!(r.as_slice().unwrap(), &[true, false]);
622 }
623
624 #[test]
625 fn test_iscomplex_real_input() {
626 let a: Array<f64, Ix1> = Array::from_vec(Ix1::new([3]), vec![1.0, 2.0, 3.0]).unwrap();
627 let r = iscomplex_real(&a).unwrap();
628 assert_eq!(r.as_slice().unwrap(), &[false, false, false]);
629 }
630
631 #[test]
632 fn test_isreal_real_input() {
633 let a: Array<f64, Ix1> = Array::from_vec(Ix1::new([3]), vec![1.0, 2.0, 3.0]).unwrap();
634 let r = isreal_real(&a).unwrap();
635 assert_eq!(r.as_slice().unwrap(), &[true, true, true]);
636 }
637
638 #[test]
639 fn test_iscomplexobj_isrealobj() {
640 let c = arr1_c64(vec![Complex64::new(1.0, 2.0)]);
641 assert!(iscomplexobj(&c));
642 assert!(!isrealobj(&c));
643
644 let r: Array<f64, Ix1> = Array::from_vec(Ix1::new([2]), vec![1.0, 2.0]).unwrap();
645 assert!(!iscomplexobj(&r));
646 assert!(isrealobj(&r));
647 }
648
649 fn approx_eq_c(a: Complex64, b: Complex64, eps: f64) -> bool {
650 (a.re - b.re).abs() <= eps && (a.im - b.im).abs() <= eps
651 }
652
653 #[test]
654 fn test_sin_complex_matches_identity() {
655 let arr = arr1_c64(vec![Complex64::new(0.0, 1.0), Complex64::new(1.0, 1.0)]);
657 let out = sin_complex(&arr).unwrap();
658 let v0 = out.iter().next().copied().unwrap();
659 assert!(approx_eq_c(
660 v0,
661 Complex64::new(0.0, 1.1752011936438014),
662 1e-10
663 ));
664 }
665
666 #[test]
667 fn test_cos_complex_matches_identity() {
668 let arr = arr1_c64(vec![Complex64::new(0.0, 1.0)]);
670 let out = cos_complex(&arr).unwrap();
671 let v0 = out.iter().next().copied().unwrap();
672 assert!(approx_eq_c(
673 v0,
674 Complex64::new(1.5430806348152437, 0.0),
675 1e-10
676 ));
677 }
678
679 #[test]
680 fn test_exp_complex_eulers_identity() {
681 let arr = arr1_c64(vec![Complex64::new(0.0, std::f64::consts::PI)]);
683 let out = exp_complex(&arr).unwrap();
684 let v0 = out.iter().next().copied().unwrap();
685 assert!(approx_eq_c(v0, Complex64::new(-1.0, 0.0), 1e-12));
686 }
687
688 #[test]
689 fn test_ln_complex_inverse_of_exp() {
690 let z = Complex64::new(0.7, 1.3);
691 let arr = arr1_c64(vec![z.exp()]);
692 let out = ln_complex(&arr).unwrap();
693 let v = out.iter().next().copied().unwrap();
694 assert!(approx_eq_c(v, z, 1e-12));
695 }
696
697 #[test]
698 fn test_sqrt_complex_principal_branch() {
699 let arr = arr1_c64(vec![Complex64::new(0.0, 1.0)]);
701 let out = sqrt_complex(&arr).unwrap();
702 let v = out.iter().next().copied().unwrap();
703 let inv_sqrt2 = 1.0_f64 / 2.0_f64.sqrt();
704 assert!(approx_eq_c(v, Complex64::new(inv_sqrt2, inv_sqrt2), 1e-12));
705 }
706
707 #[test]
708 fn test_log2_complex() {
709 let arr = arr1_c64(vec![Complex64::new(2.0, 0.0)]);
711 let out = log2_complex(&arr).unwrap();
712 let v = out.iter().next().copied().unwrap();
713 assert!(approx_eq_c(v, Complex64::new(1.0, 0.0), 1e-12));
714 }
715
716 #[test]
717 fn test_log10_complex() {
718 let arr = arr1_c64(vec![Complex64::new(100.0, 0.0)]);
720 let out = log10_complex(&arr).unwrap();
721 let v = out.iter().next().copied().unwrap();
722 assert!(approx_eq_c(v, Complex64::new(2.0, 0.0), 1e-12));
723 }
724
725 #[test]
726 fn test_expm1_complex_small_arg_precision() {
727 let z = Complex64::new(1e-12, 1e-12);
735 let arr = arr1_c64(vec![z]);
736 let out = expm1_complex(&arr).unwrap();
737 let v = out.iter().next().copied().unwrap();
738 let rel_err = (v - z).norm() / z.norm();
741 assert!(
742 rel_err < 1e-4,
743 "expm1 small-arg rel_err = {rel_err:e}, v = {v:?}"
744 );
745 }
746
747 #[test]
748 fn test_log1p_complex_small_arg_precision() {
749 let z = Complex64::new(1e-10, 1e-10);
751 let arr = arr1_c64(vec![z]);
752 let out = log1p_complex(&arr).unwrap();
753 let v = out.iter().next().copied().unwrap();
754 let rel_err = (v - z).norm() / z.norm();
755 assert!(
756 rel_err < 1e-4,
757 "log1p small-arg rel_err = {rel_err:e}, v = {v:?}"
758 );
759 }
760
761 #[test]
762 fn test_expm1_complex_moderate_arg_matches_naive() {
763 let z = Complex64::new(0.5, 0.3);
765 let arr = arr1_c64(vec![z]);
766 let out = expm1_complex(&arr).unwrap();
767 let v = out.iter().next().copied().unwrap();
768 let expected = z.exp() - Complex64::new(1.0, 0.0);
769 assert!(approx_eq_c(v, expected, 1e-12));
770 }
771
772 #[test]
773 fn test_log1p_complex_moderate_arg_matches_naive() {
774 let z = Complex64::new(0.4, 0.2);
775 let arr = arr1_c64(vec![z]);
776 let out = log1p_complex(&arr).unwrap();
777 let v = out.iter().next().copied().unwrap();
778 let expected = (Complex64::new(1.0, 0.0) + z).ln();
779 assert!(approx_eq_c(v, expected, 1e-12));
780 }
781
782 #[test]
783 fn test_power_complex_integer_exponent() {
784 let arr = arr1_c64(vec![Complex64::new(1.0, 1.0)]);
786 let out = power_complex(&arr, Complex64::new(2.0, 0.0)).unwrap();
787 let v = out.iter().next().copied().unwrap();
788 assert!(approx_eq_c(v, Complex64::new(0.0, 2.0), 1e-12));
789 }
790
791 #[test]
792 fn test_tanh_complex_round_trip() {
793 let z = Complex64::new(0.5, 0.3);
795 let arr = arr1_c64(vec![z]);
796 let t = tanh_complex(&arr).unwrap();
797 let r = atanh_complex(&t).unwrap();
798 let v = r.iter().next().copied().unwrap();
799 assert!(approx_eq_c(v, z, 1e-12));
800 }
801
802 const MAG_S: f64 = 1.3169578969248166; const HALF_PI: f64 = std::f64::consts::FRAC_PI_2;
813 const PI: f64 = std::f64::consts::PI;
814
815 type ComplexUnaryFn = fn(&Array<Complex64, Ix1>) -> FerrayResult<Array<Complex64, Ix1>>;
816
817 fn call1(z: Complex64, f: ComplexUnaryFn) -> Complex64 {
818 match f(&arr1_c64(vec![z]))
819 .ok()
820 .and_then(|a| a.iter().next().copied())
821 {
822 Some(v) => v,
823 None => Complex64::new(f64::NAN, f64::NAN),
824 }
825 }
826
827 #[test]
828 fn test_arcsin_branch_cut_signed_zero() {
829 assert!(approx_eq_c(
831 call1(Complex64::new(2.0, 0.0), asin_complex),
832 Complex64::new(HALF_PI, MAG_S),
833 1e-12
834 ));
835 assert!(approx_eq_c(
836 call1(Complex64::new(2.0, -0.0), asin_complex),
837 Complex64::new(HALF_PI, -MAG_S),
838 1e-12
839 ));
840 assert!(approx_eq_c(
841 call1(Complex64::new(-2.0, 0.0), asin_complex),
842 Complex64::new(-HALF_PI, MAG_S),
843 1e-12
844 ));
845 assert!(approx_eq_c(
846 call1(Complex64::new(-2.0, -0.0), asin_complex),
847 Complex64::new(-HALF_PI, -MAG_S),
848 1e-12
849 ));
850 }
851
852 #[test]
853 fn test_arccos_branch_cut_signed_zero() {
854 assert!(approx_eq_c(
856 call1(Complex64::new(2.0, 0.0), acos_complex),
857 Complex64::new(0.0, -MAG_S),
858 1e-12
859 ));
860 assert!(approx_eq_c(
861 call1(Complex64::new(2.0, -0.0), acos_complex),
862 Complex64::new(0.0, MAG_S),
863 1e-12
864 ));
865 assert!(approx_eq_c(
866 call1(Complex64::new(-2.0, 0.0), acos_complex),
867 Complex64::new(PI, -MAG_S),
868 1e-12
869 ));
870 assert!(approx_eq_c(
871 call1(Complex64::new(-2.0, -0.0), acos_complex),
872 Complex64::new(PI, MAG_S),
873 1e-12
874 ));
875 }
876
877 #[test]
878 fn test_arctanh_branch_cut_signed_zero() {
879 let re_m = 0.5493061443340549;
880 assert!(approx_eq_c(
881 call1(Complex64::new(2.0, 0.0), atanh_complex),
882 Complex64::new(re_m, HALF_PI),
883 1e-12
884 ));
885 assert!(approx_eq_c(
886 call1(Complex64::new(2.0, -0.0), atanh_complex),
887 Complex64::new(re_m, -HALF_PI),
888 1e-12
889 ));
890 assert!(approx_eq_c(
891 call1(Complex64::new(-2.0, 0.0), atanh_complex),
892 Complex64::new(-re_m, HALF_PI),
893 1e-12
894 ));
895 assert!(approx_eq_c(
896 call1(Complex64::new(-2.0, -0.0), atanh_complex),
897 Complex64::new(-re_m, -HALF_PI),
898 1e-12
899 ));
900 }
901
902 #[test]
903 fn test_arcsinh_branch_cut_signed_zero() {
904 assert!(approx_eq_c(
906 call1(Complex64::new(0.0, 2.0), asinh_complex),
907 Complex64::new(MAG_S, HALF_PI),
908 1e-12
909 ));
910 assert!(approx_eq_c(
911 call1(Complex64::new(-0.0, 2.0), asinh_complex),
912 Complex64::new(-MAG_S, HALF_PI),
913 1e-12
914 ));
915 assert!(approx_eq_c(
916 call1(Complex64::new(0.0, -2.0), asinh_complex),
917 Complex64::new(MAG_S, -HALF_PI),
918 1e-12
919 ));
920 assert!(approx_eq_c(
921 call1(Complex64::new(-0.0, -2.0), asinh_complex),
922 Complex64::new(-MAG_S, -HALF_PI),
923 1e-12
924 ));
925 }
926
927 #[test]
928 fn test_arctan_branch_cut_signed_zero() {
929 let im_m = 0.5493061443340549;
930 assert!(approx_eq_c(
931 call1(Complex64::new(0.0, 2.0), atan_complex),
932 Complex64::new(HALF_PI, im_m),
933 1e-12
934 ));
935 assert!(approx_eq_c(
936 call1(Complex64::new(-0.0, 2.0), atan_complex),
937 Complex64::new(-HALF_PI, im_m),
938 1e-12
939 ));
940 }
941
942 #[test]
943 fn test_arccosh_branch_cut_signed_zero() {
944 assert!(approx_eq_c(
946 call1(Complex64::new(-2.0, 0.0), acosh_complex),
947 Complex64::new(MAG_S, PI),
948 1e-12
949 ));
950 assert!(approx_eq_c(
951 call1(Complex64::new(-2.0, -0.0), acosh_complex),
952 Complex64::new(MAG_S, -PI),
953 1e-12
954 ));
955 let m = 1.0471975511965976;
957 assert!(approx_eq_c(
958 call1(Complex64::new(0.5, 0.0), acosh_complex),
959 Complex64::new(0.0, m),
960 1e-12
961 ));
962 assert!(approx_eq_c(
963 call1(Complex64::new(0.5, -0.0), acosh_complex),
964 Complex64::new(0.0, -m),
965 1e-12
966 ));
967 }
968
969 #[test]
970 fn test_arc_interior_points_unchanged() {
971 let z = Complex64::new(1.0, 1.0);
973 assert_eq!(call1(z, asin_complex), z.asin());
974 assert_eq!(call1(z, acos_complex), z.acos());
975 assert_eq!(call1(z, atanh_complex), z.atanh());
976 assert_eq!(call1(z, asinh_complex), z.asinh());
977 assert_eq!(call1(z, atan_complex), z.atan());
978 assert_eq!(call1(z, acosh_complex), z.acosh());
979 }
980
981 #[test]
982 fn test_isscalar_zero_d() {
983 use ferray_core::dimension::IxDyn;
984 let scalar: Array<f64, IxDyn> = Array::from_vec(IxDyn::new(&[]), vec![2.5]).unwrap();
985 assert!(isscalar(&scalar));
986
987 let vec: Array<f64, Ix1> = Array::from_vec(Ix1::new([3]), vec![1.0, 2.0, 3.0]).unwrap();
988 assert!(!isscalar(&vec));
989 }
990}