1use ferray_core::Array;
8use ferray_core::dimension::Dimension;
9use ferray_core::dtype::Element;
10use ferray_core::error::{FerrayError, FerrayResult};
11use num_traits::Float;
12
13use crate::helpers::{binary_float_op, unary_float_op, unary_map_op};
14
15pub fn isnan<T, D>(input: &Array<T, D>) -> FerrayResult<Array<bool, D>>
21where
22 T: Element + Float,
23 D: Dimension,
24{
25 unary_map_op(input, |x| x.is_nan())
26}
27
28pub fn isinf<T, D>(input: &Array<T, D>) -> FerrayResult<Array<bool, D>>
30where
31 T: Element + Float,
32 D: Dimension,
33{
34 unary_map_op(input, |x| x.is_infinite())
35}
36
37pub fn isfinite<T, D>(input: &Array<T, D>) -> FerrayResult<Array<bool, D>>
39where
40 T: Element + Float,
41 D: Dimension,
42{
43 unary_map_op(input, |x| x.is_finite())
44}
45
46pub fn isneginf<T, D>(input: &Array<T, D>) -> FerrayResult<Array<bool, D>>
48where
49 T: Element + Float,
50 D: Dimension,
51{
52 unary_map_op(input, |x| x.is_infinite() && x < <T as Element>::zero())
53}
54
55pub fn isposinf<T, D>(input: &Array<T, D>) -> FerrayResult<Array<bool, D>>
57where
58 T: Element + Float,
59 D: Dimension,
60{
61 unary_map_op(input, |x| x.is_infinite() && x > <T as Element>::zero())
62}
63
64pub fn signbit<T, D>(input: &Array<T, D>) -> FerrayResult<Array<bool, D>>
66where
67 T: Element + Float,
68 D: Dimension,
69{
70 unary_map_op(input, |x| x.is_sign_negative())
71}
72
73pub fn nan_to_num<T, D>(
81 input: &Array<T, D>,
82 nan: Option<T>,
83 posinf: Option<T>,
84 neginf: Option<T>,
85) -> FerrayResult<Array<T, D>>
86where
87 T: Element + Float,
88 D: Dimension,
89{
90 let nan_val = nan.unwrap_or_else(|| <T as Element>::zero());
91 let posinf_val = posinf.unwrap_or_else(T::max_value);
92 let neginf_val = neginf.unwrap_or_else(T::min_value);
93 unary_float_op(input, |x| {
94 if x.is_nan() {
95 nan_val
96 } else if x.is_infinite() {
97 if x > <T as Element>::zero() {
98 posinf_val
99 } else {
100 neginf_val
101 }
102 } else {
103 x
104 }
105 })
106}
107
108pub fn clip<T, D>(input: &Array<T, D>, a_min: T, a_max: T) -> FerrayResult<Array<T, D>>
110where
111 T: Element + Float,
112 D: Dimension,
113{
114 if a_min > a_max {
115 return Err(FerrayError::invalid_value("clip: a_min must be <= a_max"));
116 }
117 unary_float_op(input, |x| {
118 if x < a_min {
119 a_min
120 } else if x > a_max {
121 a_max
122 } else {
123 x
124 }
125 })
126}
127
128pub fn nextafter<T, D>(x1: &Array<T, D>, x2: &Array<T, D>) -> FerrayResult<Array<T, D>>
134where
135 T: Element + Float,
136 D: Dimension,
137{
138 binary_float_op(x1, x2, |a, b| {
139 if a == b {
140 b
141 } else if a < b {
142 let tiny = T::min_positive_value();
144 let eps = a.abs() * T::epsilon();
145 a + if eps > tiny { eps } else { tiny }
146 } else {
147 let tiny = T::min_positive_value();
148 let eps = a.abs() * T::epsilon();
149 a - if eps > tiny { eps } else { tiny }
150 }
151 })
152}
153
154pub fn spacing<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
156where
157 T: Element + Float,
158 D: Dimension,
159{
160 unary_float_op(input, |x| {
161 if x.is_nan() || x.is_infinite() {
162 <T as Float>::nan()
163 } else {
164 let tiny = T::min_positive_value();
165 let eps = x.abs() * T::epsilon();
166 if eps > tiny { eps } else { tiny }
167 }
168 })
169}
170
171pub fn ldexp<T, D>(x: &Array<T, D>, n: &Array<i32, D>) -> FerrayResult<Array<T, D>>
175where
176 T: Element + Float,
177 D: Dimension,
178{
179 if x.shape() != n.shape() {
180 return Err(FerrayError::shape_mismatch(format!(
181 "ldexp: shapes {:?} and {:?} do not match",
182 x.shape(),
183 n.shape()
184 )));
185 }
186 let two = <T as Element>::one() + <T as Element>::one();
187 let data: Vec<T> = x
188 .iter()
189 .zip(n.iter())
190 .map(|(&xi, &ni)| xi * two.powi(ni))
191 .collect();
192 Array::from_vec(x.dim().clone(), data)
193}
194
195pub fn frexp<T, D>(input: &Array<T, D>) -> FerrayResult<(Array<T, D>, Array<i32, D>)>
200where
201 T: Element + Float,
202 D: Dimension,
203{
204 let mut mantissas = Vec::with_capacity(input.size());
205 let mut exponents = Vec::with_capacity(input.size());
206
207 let zero = <T as Element>::zero();
208 let half = T::from(0.5).unwrap();
209 let two = T::from(2.0).unwrap();
210
211 for &x in input.iter() {
212 if x == zero || x.is_nan() || x.is_infinite() {
213 mantissas.push(x);
214 exponents.push(0);
215 continue;
216 }
217 let mut abs_x = x.abs();
218 let mut exp: i32 = 0;
219
220 while abs_x >= T::from(1.0).unwrap() {
222 abs_x = abs_x / two;
223 exp += 1;
224 }
225 while abs_x < half {
226 abs_x = abs_x * two;
227 exp -= 1;
228 }
229
230 if x.is_sign_negative() {
231 mantissas.push(-abs_x);
232 } else {
233 mantissas.push(abs_x);
234 }
235 exponents.push(exp);
236 }
237
238 Ok((
239 Array::from_vec(input.dim().clone(), mantissas)?,
240 Array::from_vec(input.dim().clone(), exponents)?,
241 ))
242}
243
244pub fn copysign<T, D>(x1: &Array<T, D>, x2: &Array<T, D>) -> FerrayResult<Array<T, D>>
246where
247 T: Element + Float,
248 D: Dimension,
249{
250 binary_float_op(x1, x2, |a, b| {
251 let mag = a.abs();
252 if b.is_sign_negative() { -mag } else { mag }
253 })
254}
255
256pub fn float_power<T, D>(x1: &Array<T, D>, x2: &Array<T, D>) -> FerrayResult<Array<T, D>>
258where
259 T: Element + Float,
260 D: Dimension,
261{
262 binary_float_op(x1, x2, T::powf)
263}
264
265pub fn maximum<T, D>(a: &Array<T, D>, b: &Array<T, D>) -> FerrayResult<Array<T, D>>
267where
268 T: Element + Float,
269 D: Dimension,
270{
271 binary_float_op(a, b, |x, y| {
272 if x.is_nan() || y.is_nan() {
273 <T as Float>::nan()
274 } else if x >= y {
275 x
276 } else {
277 y
278 }
279 })
280}
281
282pub fn minimum<T, D>(a: &Array<T, D>, b: &Array<T, D>) -> FerrayResult<Array<T, D>>
284where
285 T: Element + Float,
286 D: Dimension,
287{
288 binary_float_op(a, b, |x, y| {
289 if x.is_nan() || y.is_nan() {
290 <T as Float>::nan()
291 } else if x <= y {
292 x
293 } else {
294 y
295 }
296 })
297}
298
299pub fn fmax<T, D>(a: &Array<T, D>, b: &Array<T, D>) -> FerrayResult<Array<T, D>>
301where
302 T: Element + Float,
303 D: Dimension,
304{
305 binary_float_op(a, b, |x, y| {
306 if x.is_nan() {
307 y
308 } else if y.is_nan() || x >= y {
309 x
310 } else {
311 y
312 }
313 })
314}
315
316pub fn fmin<T, D>(a: &Array<T, D>, b: &Array<T, D>) -> FerrayResult<Array<T, D>>
318where
319 T: Element + Float,
320 D: Dimension,
321{
322 binary_float_op(a, b, |x, y| {
323 if x.is_nan() {
324 y
325 } else if y.is_nan() || x <= y {
326 x
327 } else {
328 y
329 }
330 })
331}
332
333#[cfg(feature = "f16")]
339pub fn isnan_f16<D>(input: &Array<half::f16, D>) -> FerrayResult<Array<bool, D>>
340where
341 D: Dimension,
342{
343 crate::helpers::unary_f16_to_bool_op(input, f32::is_nan)
344}
345
346#[cfg(feature = "f16")]
348pub fn isinf_f16<D>(input: &Array<half::f16, D>) -> FerrayResult<Array<bool, D>>
349where
350 D: Dimension,
351{
352 crate::helpers::unary_f16_to_bool_op(input, f32::is_infinite)
353}
354
355#[cfg(feature = "f16")]
357pub fn isfinite_f16<D>(input: &Array<half::f16, D>) -> FerrayResult<Array<bool, D>>
358where
359 D: Dimension,
360{
361 crate::helpers::unary_f16_to_bool_op(input, f32::is_finite)
362}
363
364#[cfg(feature = "f16")]
366pub fn clip_f16<D>(
367 input: &Array<half::f16, D>,
368 a_min: half::f16,
369 a_max: half::f16,
370) -> FerrayResult<Array<half::f16, D>>
371where
372 D: Dimension,
373{
374 let min_f32 = a_min.to_f32();
375 let max_f32 = a_max.to_f32();
376 if min_f32 > max_f32 {
377 return Err(FerrayError::invalid_value("clip: a_min must be <= a_max"));
378 }
379 crate::helpers::unary_f16_op(input, |x| {
380 if x < min_f32 {
381 min_f32
382 } else if x > max_f32 {
383 max_f32
384 } else {
385 x
386 }
387 })
388}
389
390#[cfg(feature = "f16")]
392pub fn nan_to_num_f16<D>(
393 input: &Array<half::f16, D>,
394 nan: Option<half::f16>,
395 posinf: Option<half::f16>,
396 neginf: Option<half::f16>,
397) -> FerrayResult<Array<half::f16, D>>
398where
399 D: Dimension,
400{
401 let nan_val = nan.unwrap_or(half::f16::ZERO).to_f32();
402 let posinf_val = posinf.unwrap_or(half::f16::MAX).to_f32();
403 let neginf_val = neginf.unwrap_or(half::f16::MIN).to_f32();
404 crate::helpers::unary_f16_op(input, |x| {
405 if x.is_nan() {
406 nan_val
407 } else if x.is_infinite() {
408 if x > 0.0 { posinf_val } else { neginf_val }
409 } else {
410 x
411 }
412 })
413}
414
415#[cfg(feature = "f16")]
417pub fn maximum_f16<D>(
418 a: &Array<half::f16, D>,
419 b: &Array<half::f16, D>,
420) -> FerrayResult<Array<half::f16, D>>
421where
422 D: Dimension,
423{
424 crate::helpers::binary_f16_op(a, b, |x, y| {
425 if x.is_nan() || y.is_nan() {
426 f32::NAN
427 } else if x >= y {
428 x
429 } else {
430 y
431 }
432 })
433}
434
435#[cfg(feature = "f16")]
437pub fn minimum_f16<D>(
438 a: &Array<half::f16, D>,
439 b: &Array<half::f16, D>,
440) -> FerrayResult<Array<half::f16, D>>
441where
442 D: Dimension,
443{
444 crate::helpers::binary_f16_op(a, b, |x, y| {
445 if x.is_nan() || y.is_nan() {
446 f32::NAN
447 } else if x <= y {
448 x
449 } else {
450 y
451 }
452 })
453}
454
455#[cfg(test)]
456mod tests {
457 use super::*;
458 use ferray_core::dimension::Ix1;
459
460 fn arr1(data: Vec<f64>) -> Array<f64, Ix1> {
461 let n = data.len();
462 Array::from_vec(Ix1::new([n]), data).unwrap()
463 }
464
465 fn arr1_i32(data: Vec<i32>) -> Array<i32, Ix1> {
466 let n = data.len();
467 Array::from_vec(Ix1::new([n]), data).unwrap()
468 }
469
470 #[test]
471 fn test_isnan() {
472 let a = arr1(vec![1.0, f64::NAN, 3.0]);
473 let r = isnan(&a).unwrap();
474 assert_eq!(r.as_slice().unwrap(), &[false, true, false]);
475 }
476
477 #[test]
478 fn test_isinf() {
479 let a = arr1(vec![1.0, f64::INFINITY, f64::NEG_INFINITY]);
480 let r = isinf(&a).unwrap();
481 assert_eq!(r.as_slice().unwrap(), &[false, true, true]);
482 }
483
484 #[test]
485 fn test_isfinite() {
486 let a = arr1(vec![1.0, f64::INFINITY, f64::NAN]);
487 let r = isfinite(&a).unwrap();
488 assert_eq!(r.as_slice().unwrap(), &[true, false, false]);
489 }
490
491 #[test]
492 fn test_isneginf() {
493 let a = arr1(vec![f64::NEG_INFINITY, f64::INFINITY, 0.0]);
494 let r = isneginf(&a).unwrap();
495 assert_eq!(r.as_slice().unwrap(), &[true, false, false]);
496 }
497
498 #[test]
499 fn test_isposinf() {
500 let a = arr1(vec![f64::NEG_INFINITY, f64::INFINITY, 0.0]);
501 let r = isposinf(&a).unwrap();
502 assert_eq!(r.as_slice().unwrap(), &[false, true, false]);
503 }
504
505 #[test]
506 fn test_nan_to_num() {
507 let a = arr1(vec![f64::NAN, f64::INFINITY, f64::NEG_INFINITY, 1.0]);
508 let r = nan_to_num(&a, None, None, None).unwrap();
509 let s = r.as_slice().unwrap();
510 assert_eq!(s[0], 0.0);
511 assert!(s[1].is_finite() && s[1] > 0.0);
512 assert!(s[2].is_finite() && s[2] < 0.0);
513 assert_eq!(s[3], 1.0);
514 }
515
516 #[test]
517 fn test_clip() {
518 let a = arr1(vec![-1.0, 0.5, 1.5, 3.0]);
519 let r = clip(&a, 0.0, 2.0).unwrap();
520 assert_eq!(r.as_slice().unwrap(), &[0.0, 0.5, 1.5, 2.0]);
521 }
522
523 #[test]
524 fn test_signbit() {
525 let a = arr1(vec![-1.0, 0.0, 1.0]);
526 let r = signbit(&a).unwrap();
527 assert_eq!(r.as_slice().unwrap(), &[true, false, false]);
528 }
529
530 #[test]
531 fn test_copysign() {
532 let a = arr1(vec![1.0, -2.0, 3.0]);
533 let b = arr1(vec![-1.0, 1.0, -1.0]);
534 let r = copysign(&a, &b).unwrap();
535 assert_eq!(r.as_slice().unwrap(), &[-1.0, 2.0, -3.0]);
536 }
537
538 #[test]
539 fn test_maximum() {
540 let a = arr1(vec![1.0, 5.0, 3.0]);
541 let b = arr1(vec![4.0, 2.0, 3.0]);
542 let r = maximum(&a, &b).unwrap();
543 assert_eq!(r.as_slice().unwrap(), &[4.0, 5.0, 3.0]);
544 }
545
546 #[test]
547 fn test_minimum() {
548 let a = arr1(vec![1.0, 5.0, 3.0]);
549 let b = arr1(vec![4.0, 2.0, 3.0]);
550 let r = minimum(&a, &b).unwrap();
551 assert_eq!(r.as_slice().unwrap(), &[1.0, 2.0, 3.0]);
552 }
553
554 #[test]
555 fn test_maximum_nan_propagation() {
556 let a = arr1(vec![1.0, f64::NAN]);
557 let b = arr1(vec![2.0, 3.0]);
558 let r = maximum(&a, &b).unwrap();
559 let s = r.as_slice().unwrap();
560 assert_eq!(s[0], 2.0);
561 assert!(s[1].is_nan());
562 }
563
564 #[test]
565 fn test_fmax_ignores_nan() {
566 let a = arr1(vec![1.0, f64::NAN]);
567 let b = arr1(vec![2.0, 3.0]);
568 let r = fmax(&a, &b).unwrap();
569 let s = r.as_slice().unwrap();
570 assert_eq!(s[0], 2.0);
571 assert_eq!(s[1], 3.0);
572 }
573
574 #[test]
575 fn test_fmin_ignores_nan() {
576 let a = arr1(vec![1.0, f64::NAN]);
577 let b = arr1(vec![2.0, 3.0]);
578 let r = fmin(&a, &b).unwrap();
579 let s = r.as_slice().unwrap();
580 assert_eq!(s[0], 1.0);
581 assert_eq!(s[1], 3.0);
582 }
583
584 #[test]
585 fn test_float_power() {
586 let a = arr1(vec![2.0, 3.0]);
587 let b = arr1(vec![3.0, 2.0]);
588 let r = float_power(&a, &b).unwrap();
589 assert_eq!(r.as_slice().unwrap(), &[8.0, 9.0]);
590 }
591
592 #[test]
593 fn test_spacing() {
594 let a = arr1(vec![1.0]);
595 let r = spacing(&a).unwrap();
596 let s = r.as_slice().unwrap();
597 assert!(s[0] > 0.0 && s[0] < 1e-10);
598 }
599
600 #[test]
601 fn test_ldexp() {
602 let x = arr1(vec![1.0, 2.0]);
603 let n = arr1_i32(vec![2, 3]);
604 let r = ldexp(&x, &n).unwrap();
605 let s = r.as_slice().unwrap();
606 assert!((s[0] - 4.0).abs() < 1e-12);
607 assert!((s[1] - 16.0).abs() < 1e-12);
608 }
609
610 #[test]
611 fn test_frexp() {
612 let a = arr1(vec![4.0]);
613 let (m, e) = frexp(&a).unwrap();
614 let ms = m.as_slice().unwrap();
615 let es = e.as_slice().unwrap();
616 assert!((ms[0] - 0.5).abs() < 1e-12);
618 assert_eq!(es[0], 3);
619 }
620
621 #[test]
622 fn test_nextafter() {
623 let a = arr1(vec![0.0]);
624 let b = arr1(vec![1.0]);
625 let r = nextafter(&a, &b).unwrap();
626 let s = r.as_slice().unwrap();
627 assert!(s[0] > 0.0);
628 }
629
630 #[cfg(feature = "f16")]
631 mod f16_tests {
632 use super::*;
633
634 fn arr1_f16(data: &[f32]) -> Array<half::f16, Ix1> {
635 let n = data.len();
636 let vals: Vec<half::f16> = data.iter().map(|&x| half::f16::from_f32(x)).collect();
637 Array::from_vec(Ix1::new([n]), vals).unwrap()
638 }
639
640 #[test]
641 fn test_isnan_f16() {
642 let a = arr1_f16(&[1.0, f32::NAN, 3.0]);
643 let r = isnan_f16(&a).unwrap();
644 assert_eq!(r.as_slice().unwrap(), &[false, true, false]);
645 }
646
647 #[test]
648 fn test_isinf_f16() {
649 let a = arr1_f16(&[1.0, f32::INFINITY, f32::NEG_INFINITY]);
650 let r = isinf_f16(&a).unwrap();
651 assert_eq!(r.as_slice().unwrap(), &[false, true, true]);
652 }
653
654 #[test]
655 fn test_isfinite_f16() {
656 let a = arr1_f16(&[1.0, f32::INFINITY, f32::NAN]);
657 let r = isfinite_f16(&a).unwrap();
658 assert_eq!(r.as_slice().unwrap(), &[true, false, false]);
659 }
660
661 #[test]
662 fn test_clip_f16() {
663 let a = arr1_f16(&[-1.0, 0.5, 1.5, 3.0]);
664 let r = clip_f16(&a, half::f16::from_f32(0.0), half::f16::from_f32(2.0)).unwrap();
665 let s = r.as_slice().unwrap();
666 assert!((s[0].to_f32() - 0.0).abs() < 0.01);
667 assert!((s[1].to_f32() - 0.5).abs() < 0.01);
668 assert!((s[2].to_f32() - 1.5).abs() < 0.01);
669 assert!((s[3].to_f32() - 2.0).abs() < 0.01);
670 }
671
672 #[test]
673 fn test_nan_to_num_f16() {
674 let a = arr1_f16(&[f32::NAN, 1.0]);
675 let r = nan_to_num_f16(&a, None, None, None).unwrap();
676 let s = r.as_slice().unwrap();
677 assert_eq!(s[0].to_f32(), 0.0);
678 assert!((s[1].to_f32() - 1.0).abs() < 0.01);
679 }
680
681 #[test]
682 fn test_maximum_f16() {
683 let a = arr1_f16(&[1.0, 5.0, 3.0]);
684 let b = arr1_f16(&[4.0, 2.0, 3.0]);
685 let r = maximum_f16(&a, &b).unwrap();
686 let s = r.as_slice().unwrap();
687 assert!((s[0].to_f32() - 4.0).abs() < 0.01);
688 assert!((s[1].to_f32() - 5.0).abs() < 0.01);
689 assert!((s[2].to_f32() - 3.0).abs() < 0.01);
690 }
691
692 #[test]
693 fn test_minimum_f16() {
694 let a = arr1_f16(&[1.0, 5.0, 3.0]);
695 let b = arr1_f16(&[4.0, 2.0, 3.0]);
696 let r = minimum_f16(&a, &b).unwrap();
697 let s = r.as_slice().unwrap();
698 assert!((s[0].to_f32() - 1.0).abs() < 0.01);
699 assert!((s[1].to_f32() - 2.0).abs() < 0.01);
700 assert!((s[2].to_f32() - 3.0).abs() < 0.01);
701 }
702 }
703}