1use ferray_core::Array;
38use ferray_core::dimension::Dimension;
39use ferray_core::dtype::Element;
40use ferray_core::error::{FerrayError, FerrayResult};
41use num_traits::Float;
42
43use crate::helpers::{binary_elementwise_op, binary_mixed_op, unary_float_op, unary_map_op};
44
45pub fn isnan<T, D>(input: &Array<T, D>) -> FerrayResult<Array<bool, D>>
51where
52 T: Element + Float,
53 D: Dimension,
54{
55 unary_map_op(input, num_traits::Float::is_nan)
56}
57
58pub fn isinf<T, D>(input: &Array<T, D>) -> FerrayResult<Array<bool, D>>
60where
61 T: Element + Float,
62 D: Dimension,
63{
64 unary_map_op(input, num_traits::Float::is_infinite)
65}
66
67pub fn isfinite<T, D>(input: &Array<T, D>) -> FerrayResult<Array<bool, D>>
69where
70 T: Element + Float,
71 D: Dimension,
72{
73 unary_map_op(input, num_traits::Float::is_finite)
74}
75
76pub fn isneginf<T, D>(input: &Array<T, D>) -> FerrayResult<Array<bool, D>>
78where
79 T: Element + Float,
80 D: Dimension,
81{
82 unary_map_op(input, |x| x.is_infinite() && x < <T as Element>::zero())
83}
84
85pub fn isposinf<T, D>(input: &Array<T, D>) -> FerrayResult<Array<bool, D>>
87where
88 T: Element + Float,
89 D: Dimension,
90{
91 unary_map_op(input, |x| x.is_infinite() && x > <T as Element>::zero())
92}
93
94pub fn signbit<T, D>(input: &Array<T, D>) -> FerrayResult<Array<bool, D>>
96where
97 T: Element + Float,
98 D: Dimension,
99{
100 unary_map_op(input, num_traits::Float::is_sign_negative)
101}
102
103pub fn nan_to_num<T, D>(
111 input: &Array<T, D>,
112 nan: Option<T>,
113 posinf: Option<T>,
114 neginf: Option<T>,
115) -> FerrayResult<Array<T, D>>
116where
117 T: Element + Float,
118 D: Dimension,
119{
120 let nan_val = nan.unwrap_or_else(|| <T as Element>::zero());
121 let posinf_val = posinf.unwrap_or_else(T::max_value);
122 let neginf_val = neginf.unwrap_or_else(T::min_value);
123 unary_float_op(input, |x| {
124 if x.is_nan() {
125 nan_val
126 } else if x.is_infinite() {
127 if x > <T as Element>::zero() {
128 posinf_val
129 } else {
130 neginf_val
131 }
132 } else {
133 x
134 }
135 })
136}
137
138pub fn clip<T, D>(input: &Array<T, D>, a_min: T, a_max: T) -> FerrayResult<Array<T, D>>
140where
141 T: Element + Float,
142 D: Dimension,
143{
144 if a_min > a_max {
145 return Err(FerrayError::invalid_value("clip: a_min must be <= a_max"));
146 }
147 unary_float_op(input, |x| {
148 if x < a_min {
149 a_min
150 } else if x > a_max {
151 a_max
152 } else {
153 x
154 }
155 })
156}
157
158pub fn clip_ord<T, D>(input: &Array<T, D>, a_min: T, a_max: T) -> FerrayResult<Array<T, D>>
165where
166 T: Element + PartialOrd + Copy,
167 D: Dimension,
168{
169 if a_min > a_max {
170 return Err(FerrayError::invalid_value(
171 "clip_ord: a_min must be <= a_max",
172 ));
173 }
174 let data: Vec<T> = input
175 .iter()
176 .map(|&x| {
177 if x < a_min {
178 a_min
179 } else if x > a_max {
180 a_max
181 } else {
182 x
183 }
184 })
185 .collect();
186 Array::from_vec(input.dim().clone(), data)
187}
188
189#[inline]
195fn nextafter_f64(a: f64, b: f64) -> f64 {
196 if a.is_nan() || b.is_nan() {
197 return f64::NAN;
198 }
199 if a == b {
200 return b;
201 }
202 if a == 0.0 {
203 return if b > 0.0 {
205 f64::from_bits(1)
206 } else {
207 f64::from_bits(1u64 | (1u64 << 63))
208 };
209 }
210 let bits = a.to_bits();
211 let new_bits = if (a < b) == (a > 0.0) {
212 bits + 1
214 } else {
215 bits - 1
217 };
218 f64::from_bits(new_bits)
219}
220
221#[inline]
223fn nextafter_f32(a: f32, b: f32) -> f32 {
224 if a.is_nan() || b.is_nan() {
225 return f32::NAN;
226 }
227 if a == b {
228 return b;
229 }
230 if a == 0.0 {
231 return if b > 0.0 {
232 f32::from_bits(1)
233 } else {
234 f32::from_bits(1u32 | (1u32 << 31))
235 };
236 }
237 let bits = a.to_bits();
238 let new_bits = if (a < b) == (a > 0.0) {
239 bits + 1
240 } else {
241 bits - 1
242 };
243 f32::from_bits(new_bits)
244}
245
246pub fn nextafter<T, D>(x1: &Array<T, D>, x2: &Array<T, D>) -> FerrayResult<Array<T, D>>
248where
249 T: Element + Float,
250 D: Dimension,
251{
252 use std::any::TypeId;
253
254 if TypeId::of::<T>() == TypeId::of::<f64>() {
255 let a_f64 = unsafe { &*std::ptr::from_ref::<Array<T, D>>(x1).cast::<Array<f64, D>>() };
257 let b_f64 = unsafe { &*std::ptr::from_ref::<Array<T, D>>(x2).cast::<Array<f64, D>>() };
258 let result = binary_elementwise_op(a_f64, b_f64, nextafter_f64)?;
259 Ok(unsafe { crate::helpers::reinterpret_array::<f64, T, D>(result) })
260 } else if TypeId::of::<T>() == TypeId::of::<f32>() {
261 let a_f32 = unsafe { &*std::ptr::from_ref::<Array<T, D>>(x1).cast::<Array<f32, D>>() };
263 let b_f32 = unsafe { &*std::ptr::from_ref::<Array<T, D>>(x2).cast::<Array<f32, D>>() };
264 let result = binary_elementwise_op(a_f32, b_f32, nextafter_f32)?;
265 Ok(unsafe { crate::helpers::reinterpret_array::<f32, T, D>(result) })
266 } else {
267 binary_elementwise_op(x1, x2, |a, b| {
269 let a64 = a.to_f64().unwrap();
270 let b64 = b.to_f64().unwrap();
271 T::from(nextafter_f64(a64, b64)).unwrap()
272 })
273 }
274}
275
276#[inline]
278fn spacing_f64(x: f64) -> f64 {
279 if x.is_nan() || x.is_infinite() {
280 return f64::NAN;
281 }
282 if x == 0.0 {
283 return f64::from_bits(1); }
285 nextafter_f64(x, f64::INFINITY.copysign(x)) - x
288}
289
290#[inline]
292fn spacing_f32(x: f32) -> f32 {
293 if x.is_nan() || x.is_infinite() {
294 return f32::NAN;
295 }
296 if x == 0.0 {
297 return f32::from_bits(1); }
299 nextafter_f32(x, f32::INFINITY.copysign(x)) - x
302}
303
304pub fn spacing<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
307where
308 T: Element + Float,
309 D: Dimension,
310{
311 use std::any::TypeId;
312
313 if TypeId::of::<T>() == TypeId::of::<f64>() {
314 let f64_input =
315 unsafe { &*std::ptr::from_ref::<Array<T, D>>(input).cast::<Array<f64, D>>() };
316 let result = unary_float_op(f64_input, spacing_f64)?;
317 Ok(unsafe { crate::helpers::reinterpret_array::<f64, T, D>(result) })
318 } else if TypeId::of::<T>() == TypeId::of::<f32>() {
319 let f32_input =
320 unsafe { &*std::ptr::from_ref::<Array<T, D>>(input).cast::<Array<f32, D>>() };
321 let result = unary_float_op(f32_input, spacing_f32)?;
322 Ok(unsafe { crate::helpers::reinterpret_array::<f32, T, D>(result) })
323 } else {
324 unary_float_op(input, |x| {
326 let x64 = x.to_f64().unwrap();
327 T::from(spacing_f64(x64)).unwrap()
328 })
329 }
330}
331
332pub fn ldexp<T, D>(x: &Array<T, D>, n: &Array<i32, D>) -> FerrayResult<Array<T, D>>
336where
337 T: Element + Float,
338 D: Dimension,
339{
340 let two = <T as Element>::one() + <T as Element>::one();
341 binary_mixed_op(x, n, move |xi, ni| xi * two.powi(ni))
342}
343
344#[inline]
348fn frexp_f64(x: f64) -> (f64, i32) {
349 if x == 0.0 || x.is_nan() || x.is_infinite() {
350 return (x, 0);
351 }
352 let bits = x.to_bits();
353 let sign = bits & (1u64 << 63);
354 let mut exp = ((bits >> 52) & 0x7FF) as i32;
355 if exp == 0 {
356 let nx = x * (1u64 << 63) as f64 * 2.0;
358 let nbits = nx.to_bits();
359 exp = ((nbits >> 52) & 0x7FF) as i32 - 64;
360 let frac = f64::from_bits((nbits & !(0x7FFu64 << 52)) | (0x3FEu64 << 52));
361 return (if sign != 0 { -frac.abs() } else { frac }, exp - 0x3FE);
362 }
363 let frac = f64::from_bits((bits & !(0x7FFu64 << 52)) | (0x3FEu64 << 52));
365 (frac, exp - 0x3FE)
367}
368
369#[inline]
373fn frexp_f32(x: f32) -> (f32, i32) {
374 if x == 0.0 || x.is_nan() || x.is_infinite() {
375 return (x, 0);
376 }
377 let bits = x.to_bits();
378 let sign = bits & (1u32 << 31);
379 let mut exp = ((bits >> 23) & 0xFF) as i32;
380 if exp == 0 {
381 let nx = x * (1u32 << 31) as f32 * 2.0;
383 let nbits = nx.to_bits();
384 exp = ((nbits >> 23) & 0xFF) as i32 - 32;
385 let frac = f32::from_bits((nbits & !(0xFFu32 << 23)) | (0x7Eu32 << 23));
386 return (if sign != 0 { -frac.abs() } else { frac }, exp - 0x7E);
387 }
388 let frac = f32::from_bits((bits & !(0xFFu32 << 23)) | (0x7Eu32 << 23));
389 (frac, exp - 0x7E)
390}
391
392pub fn frexp<T, D>(input: &Array<T, D>) -> FerrayResult<(Array<T, D>, Array<i32, D>)>
400where
401 T: Element + Float,
402 D: Dimension,
403{
404 use std::any::TypeId;
405
406 if TypeId::of::<T>() == TypeId::of::<f64>() {
407 let f64_input =
409 unsafe { &*std::ptr::from_ref::<Array<T, D>>(input).cast::<Array<f64, D>>() };
410 let mut mantissas = Vec::with_capacity(f64_input.size());
411 let mut exponents = Vec::with_capacity(f64_input.size());
412 for &x in f64_input.iter() {
413 let (m, e) = frexp_f64(x);
414 mantissas.push(m);
415 exponents.push(e);
416 }
417 let m_arr: Array<f64, D> = Array::from_vec(f64_input.dim().clone(), mantissas)?;
418 let e_arr = Array::from_vec(f64_input.dim().clone(), exponents)?;
419 let m_result = unsafe { crate::helpers::reinterpret_array::<f64, T, D>(m_arr) };
420 Ok((m_result, e_arr))
421 } else if TypeId::of::<T>() == TypeId::of::<f32>() {
422 let f32_input =
424 unsafe { &*std::ptr::from_ref::<Array<T, D>>(input).cast::<Array<f32, D>>() };
425 let mut mantissas = Vec::with_capacity(f32_input.size());
426 let mut exponents = Vec::with_capacity(f32_input.size());
427 for &x in f32_input.iter() {
428 let (m, e) = frexp_f32(x);
429 mantissas.push(m);
430 exponents.push(e);
431 }
432 let m_arr: Array<f32, D> = Array::from_vec(f32_input.dim().clone(), mantissas)?;
433 let e_arr = Array::from_vec(f32_input.dim().clone(), exponents)?;
434 let m_result = unsafe { crate::helpers::reinterpret_array::<f32, T, D>(m_arr) };
435 Ok((m_result, e_arr))
436 } else {
437 let mut mantissas = Vec::with_capacity(input.size());
439 let mut exponents = Vec::with_capacity(input.size());
440 for &x in input.iter() {
441 let x64 = x.to_f64().unwrap();
442 let (m64, e) = frexp_f64(x64);
443 mantissas.push(T::from(m64).unwrap());
444 exponents.push(e);
445 }
446 Ok((
447 Array::from_vec(input.dim().clone(), mantissas)?,
448 Array::from_vec(input.dim().clone(), exponents)?,
449 ))
450 }
451}
452
453pub fn modf<T, D>(input: &Array<T, D>) -> FerrayResult<(Array<T, D>, Array<T, D>)>
467where
468 T: Element + Float,
469 D: Dimension,
470{
471 let mut fracs = Vec::with_capacity(input.size());
472 let mut ints = Vec::with_capacity(input.size());
473 for &x in input.iter() {
474 if x.is_nan() {
475 fracs.push(x);
476 ints.push(x);
477 } else if x.is_infinite() {
478 let zero = <T as num_traits::Zero>::zero();
480 fracs.push(if x.is_sign_positive() { zero } else { -zero });
481 ints.push(x);
482 } else {
483 let i = x.trunc();
484 ints.push(i);
485 fracs.push(x - i);
486 }
487 }
488 Ok((
489 Array::from_vec(input.dim().clone(), fracs)?,
490 Array::from_vec(input.dim().clone(), ints)?,
491 ))
492}
493
494pub fn copysign<T, D>(x1: &Array<T, D>, x2: &Array<T, D>) -> FerrayResult<Array<T, D>>
496where
497 T: Element + Float,
498 D: Dimension,
499{
500 binary_elementwise_op(x1, x2, |a, b| {
501 let mag = a.abs();
502 if b.is_sign_negative() { -mag } else { mag }
503 })
504}
505
506pub fn float_power<T, D>(x1: &Array<T, D>, x2: &Array<T, D>) -> FerrayResult<Array<T, D>>
508where
509 T: Element + Float,
510 D: Dimension,
511{
512 binary_elementwise_op(x1, x2, T::powf)
513}
514
515pub fn maximum<T, D>(a: &Array<T, D>, b: &Array<T, D>) -> FerrayResult<Array<T, D>>
517where
518 T: Element + Float,
519 D: Dimension,
520{
521 binary_elementwise_op(a, b, |x, y| {
522 if x.is_nan() || y.is_nan() {
523 <T as Float>::nan()
524 } else if x >= y {
525 x
526 } else {
527 y
528 }
529 })
530}
531
532pub fn minimum<T, D>(a: &Array<T, D>, b: &Array<T, D>) -> FerrayResult<Array<T, D>>
534where
535 T: Element + Float,
536 D: Dimension,
537{
538 binary_elementwise_op(a, b, |x, y| {
539 if x.is_nan() || y.is_nan() {
540 <T as Float>::nan()
541 } else if x <= y {
542 x
543 } else {
544 y
545 }
546 })
547}
548
549pub fn maximum_ord<T, D>(a: &Array<T, D>, b: &Array<T, D>) -> FerrayResult<Array<T, D>>
555where
556 T: Element + Copy + PartialOrd,
557 D: Dimension,
558{
559 binary_elementwise_op(a, b, |x, y| if x >= y { x } else { y })
560}
561
562pub fn minimum_ord<T, D>(a: &Array<T, D>, b: &Array<T, D>) -> FerrayResult<Array<T, D>>
564where
565 T: Element + Copy + PartialOrd,
566 D: Dimension,
567{
568 binary_elementwise_op(a, b, |x, y| if x <= y { x } else { y })
569}
570
571pub fn fmax<T, D>(a: &Array<T, D>, b: &Array<T, D>) -> FerrayResult<Array<T, D>>
573where
574 T: Element + Float,
575 D: Dimension,
576{
577 binary_elementwise_op(a, b, |x, y| {
578 if x.is_nan() {
579 y
580 } else if y.is_nan() || x >= y {
581 x
582 } else {
583 y
584 }
585 })
586}
587
588pub fn fmin<T, D>(a: &Array<T, D>, b: &Array<T, D>) -> FerrayResult<Array<T, D>>
590where
591 T: Element + Float,
592 D: Dimension,
593{
594 binary_elementwise_op(a, b, |x, y| {
595 if x.is_nan() {
596 y
597 } else if y.is_nan() || x <= y {
598 x
599 } else {
600 y
601 }
602 })
603}
604
605#[cfg(feature = "f16")]
611pub fn isnan_f16<D>(input: &Array<half::f16, D>) -> FerrayResult<Array<bool, D>>
612where
613 D: Dimension,
614{
615 crate::helpers::unary_f16_to_bool_op(input, f32::is_nan)
616}
617
618#[cfg(feature = "f16")]
620pub fn isinf_f16<D>(input: &Array<half::f16, D>) -> FerrayResult<Array<bool, D>>
621where
622 D: Dimension,
623{
624 crate::helpers::unary_f16_to_bool_op(input, f32::is_infinite)
625}
626
627#[cfg(feature = "f16")]
629pub fn isfinite_f16<D>(input: &Array<half::f16, D>) -> FerrayResult<Array<bool, D>>
630where
631 D: Dimension,
632{
633 crate::helpers::unary_f16_to_bool_op(input, f32::is_finite)
634}
635
636#[cfg(feature = "f16")]
638pub fn clip_f16<D>(
639 input: &Array<half::f16, D>,
640 a_min: half::f16,
641 a_max: half::f16,
642) -> FerrayResult<Array<half::f16, D>>
643where
644 D: Dimension,
645{
646 let min_f32 = a_min.to_f32();
647 let max_f32 = a_max.to_f32();
648 if min_f32 > max_f32 {
649 return Err(FerrayError::invalid_value("clip: a_min must be <= a_max"));
650 }
651 crate::helpers::unary_f16_op(input, |x| {
652 if x < min_f32 {
653 min_f32
654 } else if x > max_f32 {
655 max_f32
656 } else {
657 x
658 }
659 })
660}
661
662#[cfg(feature = "f16")]
664pub fn nan_to_num_f16<D>(
665 input: &Array<half::f16, D>,
666 nan: Option<half::f16>,
667 posinf: Option<half::f16>,
668 neginf: Option<half::f16>,
669) -> FerrayResult<Array<half::f16, D>>
670where
671 D: Dimension,
672{
673 let nan_val = nan.unwrap_or(half::f16::ZERO).to_f32();
674 let posinf_val = posinf.unwrap_or(half::f16::MAX).to_f32();
675 let neginf_val = neginf.unwrap_or(half::f16::MIN).to_f32();
676 crate::helpers::unary_f16_op(input, |x| {
677 if x.is_nan() {
678 nan_val
679 } else if x.is_infinite() {
680 if x > 0.0 { posinf_val } else { neginf_val }
681 } else {
682 x
683 }
684 })
685}
686
687#[cfg(feature = "f16")]
689pub fn maximum_f16<D>(
690 a: &Array<half::f16, D>,
691 b: &Array<half::f16, D>,
692) -> FerrayResult<Array<half::f16, D>>
693where
694 D: Dimension,
695{
696 crate::helpers::binary_f16_op(a, b, |x, y| {
697 if x.is_nan() || y.is_nan() {
698 f32::NAN
699 } else if x >= y {
700 x
701 } else {
702 y
703 }
704 })
705}
706
707#[cfg(feature = "f16")]
709pub fn minimum_f16<D>(
710 a: &Array<half::f16, D>,
711 b: &Array<half::f16, D>,
712) -> FerrayResult<Array<half::f16, D>>
713where
714 D: Dimension,
715{
716 crate::helpers::binary_f16_op(a, b, |x, y| {
717 if x.is_nan() || y.is_nan() {
718 f32::NAN
719 } else if x <= y {
720 x
721 } else {
722 y
723 }
724 })
725}
726
727#[cfg(test)]
728mod tests {
729 use super::*;
730 use ferray_core::dimension::Ix1;
731
732 use crate::test_util::arr1;
733
734 fn arr1_i32(data: Vec<i32>) -> Array<i32, Ix1> {
735 let n = data.len();
736 Array::from_vec(Ix1::new([n]), data).unwrap()
737 }
738
739 #[test]
740 fn test_isnan() {
741 let a = arr1(vec![1.0, f64::NAN, 3.0]);
742 let r = isnan(&a).unwrap();
743 assert_eq!(r.as_slice().unwrap(), &[false, true, false]);
744 }
745
746 #[test]
747 fn test_isinf() {
748 let a = arr1(vec![1.0, f64::INFINITY, f64::NEG_INFINITY]);
749 let r = isinf(&a).unwrap();
750 assert_eq!(r.as_slice().unwrap(), &[false, true, true]);
751 }
752
753 #[test]
754 fn test_isfinite() {
755 let a = arr1(vec![1.0, f64::INFINITY, f64::NAN]);
756 let r = isfinite(&a).unwrap();
757 assert_eq!(r.as_slice().unwrap(), &[true, false, false]);
758 }
759
760 #[test]
761 fn test_isneginf() {
762 let a = arr1(vec![f64::NEG_INFINITY, f64::INFINITY, 0.0]);
763 let r = isneginf(&a).unwrap();
764 assert_eq!(r.as_slice().unwrap(), &[true, false, false]);
765 }
766
767 #[test]
768 fn test_isposinf() {
769 let a = arr1(vec![f64::NEG_INFINITY, f64::INFINITY, 0.0]);
770 let r = isposinf(&a).unwrap();
771 assert_eq!(r.as_slice().unwrap(), &[false, true, false]);
772 }
773
774 #[test]
775 fn test_nan_to_num() {
776 let a = arr1(vec![f64::NAN, f64::INFINITY, f64::NEG_INFINITY, 1.0]);
777 let r = nan_to_num(&a, None, None, None).unwrap();
778 let s = r.as_slice().unwrap();
779 assert_eq!(s[0], 0.0);
780 assert!(s[1].is_finite() && s[1] > 0.0);
781 assert!(s[2].is_finite() && s[2] < 0.0);
782 assert_eq!(s[3], 1.0);
783 }
784
785 #[test]
786 fn test_clip() {
787 let a = arr1(vec![-1.0, 0.5, 1.5, 3.0]);
788 let r = clip(&a, 0.0, 2.0).unwrap();
789 assert_eq!(r.as_slice().unwrap(), &[0.0, 0.5, 1.5, 2.0]);
790 }
791
792 #[test]
793 fn test_clip_ord_integer() {
794 let a = Array::<i32, Ix1>::from_vec(Ix1::new([5]), vec![-10, 0, 5, 100, 255]).unwrap();
795 let r = clip_ord(&a, 0, 200).unwrap();
796 assert_eq!(r.as_slice().unwrap(), &[0, 0, 5, 100, 200]);
797 }
798
799 #[test]
800 fn test_clip_ord_u8() {
801 let a = Array::<u8, Ix1>::from_vec(Ix1::new([4]), vec![0, 50, 200, 255]).unwrap();
802 let r = clip_ord(&a, 10, 128).unwrap();
803 assert_eq!(r.as_slice().unwrap(), &[10, 50, 128, 128]);
804 }
805
806 #[test]
807 fn test_signbit() {
808 let a = arr1(vec![-1.0, 0.0, 1.0]);
809 let r = signbit(&a).unwrap();
810 assert_eq!(r.as_slice().unwrap(), &[true, false, false]);
811 }
812
813 #[test]
814 fn test_copysign() {
815 let a = arr1(vec![1.0, -2.0, 3.0]);
816 let b = arr1(vec![-1.0, 1.0, -1.0]);
817 let r = copysign(&a, &b).unwrap();
818 assert_eq!(r.as_slice().unwrap(), &[-1.0, 2.0, -3.0]);
819 }
820
821 #[test]
822 fn test_maximum() {
823 let a = arr1(vec![1.0, 5.0, 3.0]);
824 let b = arr1(vec![4.0, 2.0, 3.0]);
825 let r = maximum(&a, &b).unwrap();
826 assert_eq!(r.as_slice().unwrap(), &[4.0, 5.0, 3.0]);
827 }
828
829 #[test]
830 fn test_minimum() {
831 let a = arr1(vec![1.0, 5.0, 3.0]);
832 let b = arr1(vec![4.0, 2.0, 3.0]);
833 let r = minimum(&a, &b).unwrap();
834 assert_eq!(r.as_slice().unwrap(), &[1.0, 2.0, 3.0]);
835 }
836
837 #[test]
838 fn test_maximum_nan_propagation() {
839 let a = arr1(vec![1.0, f64::NAN]);
840 let b = arr1(vec![2.0, 3.0]);
841 let r = maximum(&a, &b).unwrap();
842 let s = r.as_slice().unwrap();
843 assert_eq!(s[0], 2.0);
844 assert!(s[1].is_nan());
845 }
846
847 #[test]
848 fn test_fmax_ignores_nan() {
849 let a = arr1(vec![1.0, f64::NAN]);
850 let b = arr1(vec![2.0, 3.0]);
851 let r = fmax(&a, &b).unwrap();
852 let s = r.as_slice().unwrap();
853 assert_eq!(s[0], 2.0);
854 assert_eq!(s[1], 3.0);
855 }
856
857 #[test]
858 fn test_fmin_ignores_nan() {
859 let a = arr1(vec![1.0, f64::NAN]);
860 let b = arr1(vec![2.0, 3.0]);
861 let r = fmin(&a, &b).unwrap();
862 let s = r.as_slice().unwrap();
863 assert_eq!(s[0], 1.0);
864 assert_eq!(s[1], 3.0);
865 }
866
867 #[test]
868 fn test_float_power() {
869 let a = arr1(vec![2.0, 3.0]);
870 let b = arr1(vec![3.0, 2.0]);
871 let r = float_power(&a, &b).unwrap();
872 assert_eq!(r.as_slice().unwrap(), &[8.0, 9.0]);
873 }
874
875 #[test]
876 fn test_spacing() {
877 let a = arr1(vec![1.0]);
878 let r = spacing(&a).unwrap();
879 let s = r.as_slice().unwrap();
880 assert_eq!(s[0], f64::EPSILON);
882 }
883
884 #[test]
885 fn test_spacing_zero() {
886 let a = arr1(vec![0.0]);
887 let r = spacing(&a).unwrap();
888 let s = r.as_slice().unwrap();
889 assert_eq!(s[0], f64::from_bits(1));
891 }
892
893 #[test]
894 fn test_spacing_nan_inf() {
895 let a = arr1(vec![f64::NAN, f64::INFINITY, f64::NEG_INFINITY]);
896 let r = spacing(&a).unwrap();
897 let s = r.as_slice().unwrap();
898 assert!(s[0].is_nan());
899 assert!(s[1].is_nan());
900 assert!(s[2].is_nan());
901 }
902
903 #[test]
904 fn test_ldexp() {
905 let x = arr1(vec![1.0, 2.0]);
906 let n = arr1_i32(vec![2, 3]);
907 let r = ldexp(&x, &n).unwrap();
908 let s = r.as_slice().unwrap();
909 assert!((s[0] - 4.0).abs() < 1e-12);
910 assert!((s[1] - 16.0).abs() < 1e-12);
911 }
912
913 #[test]
914 fn test_ldexp_broadcasts() {
915 use ferray_core::dimension::Ix2;
916 let x = Array::<f64, Ix2>::from_vec(Ix2::new([2, 1]), vec![1.0, 3.0]).unwrap();
917 let n = Array::<i32, Ix2>::from_vec(Ix2::new([1, 3]), vec![1, 2, 3]).unwrap();
918 let r = ldexp(&x, &n).unwrap();
919 assert_eq!(r.shape(), &[2, 3]);
920 let v: Vec<f64> = r.iter().copied().collect();
922 assert_eq!(v, vec![2.0, 4.0, 8.0, 6.0, 12.0, 24.0]);
923 }
924
925 #[test]
926 fn test_frexp() {
927 let a = arr1(vec![4.0]);
928 let (m, e) = frexp(&a).unwrap();
929 let ms = m.as_slice().unwrap();
930 let es = e.as_slice().unwrap();
931 assert!((ms[0] - 0.5).abs() < 1e-12);
933 assert_eq!(es[0], 3);
934 }
935
936 #[test]
937 fn test_nextafter_zero_toward_positive() {
938 let a = arr1(vec![0.0]);
939 let b = arr1(vec![1.0]);
940 let r = nextafter(&a, &b).unwrap();
941 let s = r.as_slice().unwrap();
942 assert_eq!(s[0], f64::from_bits(1));
944 }
945
946 #[test]
947 fn test_nextafter_zero_toward_negative() {
948 let a = arr1(vec![0.0]);
949 let b = arr1(vec![-1.0]);
950 let r = nextafter(&a, &b).unwrap();
951 let s = r.as_slice().unwrap();
952 assert_eq!(s[0], f64::from_bits(1u64 | (1u64 << 63)));
954 }
955
956 #[test]
957 fn test_nextafter_equal() {
958 let a = arr1(vec![1.0]);
959 let b = arr1(vec![1.0]);
960 let r = nextafter(&a, &b).unwrap();
961 let s = r.as_slice().unwrap();
962 assert_eq!(s[0], 1.0);
963 }
964
965 #[test]
966 fn test_nextafter_nan() {
967 let a = arr1(vec![f64::NAN, 1.0]);
968 let b = arr1(vec![1.0, f64::NAN]);
969 let r = nextafter(&a, &b).unwrap();
970 let s = r.as_slice().unwrap();
971 assert!(s[0].is_nan());
972 assert!(s[1].is_nan());
973 }
974
975 #[test]
976 fn test_nextafter_one_toward_two() {
977 let a = arr1(vec![1.0]);
978 let b = arr1(vec![2.0]);
979 let r = nextafter(&a, &b).unwrap();
980 let s = r.as_slice().unwrap();
981 assert_eq!(s[0], 1.0 + f64::EPSILON);
983 }
984
985 #[test]
986 fn test_nextafter_negative() {
987 let a = arr1(vec![-1.0]);
988 let b = arr1(vec![-2.0]);
989 let r = nextafter(&a, &b).unwrap();
990 let s = r.as_slice().unwrap();
991 assert_eq!(s[0], -1.0 - f64::EPSILON);
993 }
994
995 #[cfg(feature = "f16")]
996 mod f16_tests {
997 use super::*;
998
999 fn arr1_f16(data: &[f32]) -> Array<half::f16, Ix1> {
1000 let n = data.len();
1001 let vals: Vec<half::f16> = data.iter().map(|&x| half::f16::from_f32(x)).collect();
1002 Array::from_vec(Ix1::new([n]), vals).unwrap()
1003 }
1004
1005 #[test]
1006 fn test_isnan_f16() {
1007 let a = arr1_f16(&[1.0, f32::NAN, 3.0]);
1008 let r = isnan_f16(&a).unwrap();
1009 assert_eq!(r.as_slice().unwrap(), &[false, true, false]);
1010 }
1011
1012 #[test]
1013 fn test_isinf_f16() {
1014 let a = arr1_f16(&[1.0, f32::INFINITY, f32::NEG_INFINITY]);
1015 let r = isinf_f16(&a).unwrap();
1016 assert_eq!(r.as_slice().unwrap(), &[false, true, true]);
1017 }
1018
1019 #[test]
1020 fn test_isfinite_f16() {
1021 let a = arr1_f16(&[1.0, f32::INFINITY, f32::NAN]);
1022 let r = isfinite_f16(&a).unwrap();
1023 assert_eq!(r.as_slice().unwrap(), &[true, false, false]);
1024 }
1025
1026 #[test]
1027 fn test_clip_f16() {
1028 let a = arr1_f16(&[-1.0, 0.5, 1.5, 3.0]);
1029 let r = clip_f16(&a, half::f16::from_f32(0.0), half::f16::from_f32(2.0)).unwrap();
1030 let s = r.as_slice().unwrap();
1031 assert!((s[0].to_f32() - 0.0).abs() < 0.01);
1032 assert!((s[1].to_f32() - 0.5).abs() < 0.01);
1033 assert!((s[2].to_f32() - 1.5).abs() < 0.01);
1034 assert!((s[3].to_f32() - 2.0).abs() < 0.01);
1035 }
1036
1037 #[test]
1038 fn test_nan_to_num_f16() {
1039 let a = arr1_f16(&[f32::NAN, 1.0]);
1040 let r = nan_to_num_f16(&a, None, None, None).unwrap();
1041 let s = r.as_slice().unwrap();
1042 assert_eq!(s[0].to_f32(), 0.0);
1043 assert!((s[1].to_f32() - 1.0).abs() < 0.01);
1044 }
1045
1046 #[test]
1047 fn test_maximum_f16() {
1048 let a = arr1_f16(&[1.0, 5.0, 3.0]);
1049 let b = arr1_f16(&[4.0, 2.0, 3.0]);
1050 let r = maximum_f16(&a, &b).unwrap();
1051 let s = r.as_slice().unwrap();
1052 assert!((s[0].to_f32() - 4.0).abs() < 0.01);
1053 assert!((s[1].to_f32() - 5.0).abs() < 0.01);
1054 assert!((s[2].to_f32() - 3.0).abs() < 0.01);
1055 }
1056
1057 #[test]
1058 fn test_minimum_f16() {
1059 let a = arr1_f16(&[1.0, 5.0, 3.0]);
1060 let b = arr1_f16(&[4.0, 2.0, 3.0]);
1061 let r = minimum_f16(&a, &b).unwrap();
1062 let s = r.as_slice().unwrap();
1063 assert!((s[0].to_f32() - 1.0).abs() < 0.01);
1064 assert!((s[1].to_f32() - 2.0).abs() < 0.01);
1065 assert!((s[2].to_f32() - 3.0).abs() < 0.01);
1066 }
1067 }
1068
1069 #[test]
1072 fn test_modf_positive() {
1073 let a = arr1(vec![3.5, 1.25, 0.0]);
1074 let (frac, int) = modf(&a).unwrap();
1075 let f = frac.as_slice().unwrap();
1076 let i = int.as_slice().unwrap();
1077 assert!((f[0] - 0.5).abs() < 1e-12);
1078 assert!((f[1] - 0.25).abs() < 1e-12);
1079 assert_eq!(f[2], 0.0);
1080 assert_eq!(i, &[3.0, 1.0, 0.0]);
1081 }
1082
1083 #[test]
1084 fn test_modf_negative() {
1085 let a = arr1(vec![-3.5, -1.25]);
1086 let (frac, int) = modf(&a).unwrap();
1087 let f = frac.as_slice().unwrap();
1088 let i = int.as_slice().unwrap();
1089 assert!((f[0] - (-0.5)).abs() < 1e-12);
1090 assert!((f[1] - (-0.25)).abs() < 1e-12);
1091 assert_eq!(i, &[-3.0, -1.0]);
1092 }
1093
1094 #[test]
1095 fn test_modf_nan() {
1096 let a = arr1(vec![f64::NAN]);
1097 let (frac, int) = modf(&a).unwrap();
1098 assert!(frac.as_slice().unwrap()[0].is_nan());
1099 assert!(int.as_slice().unwrap()[0].is_nan());
1100 }
1101
1102 #[test]
1103 fn test_modf_infinity() {
1104 let a = arr1(vec![f64::INFINITY, f64::NEG_INFINITY]);
1105 let (frac, int) = modf(&a).unwrap();
1106 let f = frac.as_slice().unwrap();
1107 let i = int.as_slice().unwrap();
1108 assert_eq!(f[0], 0.0);
1109 assert!(f[0].is_sign_positive());
1110 assert_eq!(f[1], 0.0);
1111 assert!(f[1].is_sign_negative());
1112 assert!(i[0].is_infinite() && i[0].is_sign_positive());
1113 assert!(i[1].is_infinite() && i[1].is_sign_negative());
1114 }
1115
1116 #[test]
1117 fn test_modf_f32() {
1118 let a: Array<f32, Ix1> = Array::from_vec(Ix1::new([3]), vec![2.75_f32, -0.5, 1.0]).unwrap();
1119 let (frac, int) = modf(&a).unwrap();
1120 let f = frac.as_slice().unwrap();
1121 let i = int.as_slice().unwrap();
1122 assert!((f[0] - 0.75).abs() < 1e-6);
1123 assert!((f[1] - (-0.5)).abs() < 1e-6);
1124 assert_eq!(f[2], 0.0);
1125 assert_eq!(i, &[2.0_f32, 0.0, 1.0]);
1126 }
1127}