1use ferray_core::Array;
40use ferray_core::dimension::Dimension;
41use ferray_core::dtype::Element;
42use ferray_core::error::FerrayResult;
43use num_traits::Float;
44
45use crate::cr_math::CrMath;
46use crate::helpers::{
47 binary_elementwise_op, unary_float_op, unary_float_op_compute, unary_float_op_into_compute,
48};
49
50pub fn sin<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
62where
63 T: Element + Float,
64 D: Dimension,
65{
66 unary_float_op_compute(input, T::sin)
67}
68
69pub fn sin_into<T, D>(input: &Array<T, D>, out: &mut Array<T, D>) -> FerrayResult<()>
71where
72 T: Element + Float,
73 D: Dimension,
74{
75 unary_float_op_into_compute(input, out, "sin", T::sin)
76}
77
78pub fn cos<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
80where
81 T: Element + Float,
82 D: Dimension,
83{
84 unary_float_op_compute(input, T::cos)
85}
86
87pub fn cos_into<T, D>(input: &Array<T, D>, out: &mut Array<T, D>) -> FerrayResult<()>
89where
90 T: Element + Float,
91 D: Dimension,
92{
93 unary_float_op_into_compute(input, out, "cos", T::cos)
94}
95
96pub fn sin_fast<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
108where
109 T: Element + Float,
110 D: Dimension,
111{
112 use std::any::TypeId;
113 if TypeId::of::<T>() == TypeId::of::<f64>() {
114 let f64_input =
116 unsafe { &*std::ptr::from_ref::<Array<T, D>>(input).cast::<Array<f64, D>>() };
117 let n = f64_input.size();
118 let result = if let Some(slice) = f64_input.as_slice() {
119 let mut data = vec![0.0_f64; n];
120 crate::fast_trig::sin_fast_batch_f64(slice, &mut data);
121 Array::from_vec(f64_input.dim().clone(), data)?
122 } else {
123 let data: Vec<f64> = f64_input
124 .iter()
125 .map(|&x| crate::fast_trig::sin_fast_f64(x))
126 .collect();
127 Array::from_vec(f64_input.dim().clone(), data)?
128 };
129 Ok(unsafe { crate::helpers::reinterpret_array::<f64, T, D>(result) })
131 } else if TypeId::of::<T>() == TypeId::of::<f32>() {
132 let f32_input =
134 unsafe { &*std::ptr::from_ref::<Array<T, D>>(input).cast::<Array<f32, D>>() };
135 let n = f32_input.size();
136 let result = if let Some(slice) = f32_input.as_slice() {
137 let mut data = vec![0.0_f32; n];
138 crate::fast_trig::sin_fast_batch_f32(slice, &mut data);
139 Array::from_vec(f32_input.dim().clone(), data)?
140 } else {
141 let data: Vec<f32> = f32_input
142 .iter()
143 .map(|&x| crate::fast_trig::sin_fast_f32(x))
144 .collect();
145 Array::from_vec(f32_input.dim().clone(), data)?
146 };
147 Ok(unsafe { crate::helpers::reinterpret_array::<f32, T, D>(result) })
149 } else {
150 unary_float_op_compute(input, T::sin)
152 }
153}
154
155pub fn cos_fast<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
157where
158 T: Element + Float,
159 D: Dimension,
160{
161 use std::any::TypeId;
162 if TypeId::of::<T>() == TypeId::of::<f64>() {
163 let f64_input =
165 unsafe { &*std::ptr::from_ref::<Array<T, D>>(input).cast::<Array<f64, D>>() };
166 let n = f64_input.size();
167 let result = if let Some(slice) = f64_input.as_slice() {
168 let mut data = vec![0.0_f64; n];
169 crate::fast_trig::cos_fast_batch_f64(slice, &mut data);
170 Array::from_vec(f64_input.dim().clone(), data)?
171 } else {
172 let data: Vec<f64> = f64_input
173 .iter()
174 .map(|&x| crate::fast_trig::cos_fast_f64(x))
175 .collect();
176 Array::from_vec(f64_input.dim().clone(), data)?
177 };
178 Ok(unsafe { crate::helpers::reinterpret_array::<f64, T, D>(result) })
180 } else if TypeId::of::<T>() == TypeId::of::<f32>() {
181 let f32_input =
183 unsafe { &*std::ptr::from_ref::<Array<T, D>>(input).cast::<Array<f32, D>>() };
184 let n = f32_input.size();
185 let result = if let Some(slice) = f32_input.as_slice() {
186 let mut data = vec![0.0_f32; n];
187 crate::fast_trig::cos_fast_batch_f32(slice, &mut data);
188 Array::from_vec(f32_input.dim().clone(), data)?
189 } else {
190 let data: Vec<f32> = f32_input
191 .iter()
192 .map(|&x| crate::fast_trig::cos_fast_f32(x))
193 .collect();
194 Array::from_vec(f32_input.dim().clone(), data)?
195 };
196 Ok(unsafe { crate::helpers::reinterpret_array::<f32, T, D>(result) })
198 } else {
199 unary_float_op_compute(input, T::cos)
201 }
202}
203
204pub fn tan<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
206where
207 T: Element + Float,
208 D: Dimension,
209{
210 unary_float_op_compute(input, T::tan)
211}
212
213pub fn arcsin<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
215where
216 T: Element + Float + CrMath,
217 D: Dimension,
218{
219 unary_float_op_compute(input, T::cr_asin)
220}
221
222pub fn arccos<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
224where
225 T: Element + Float + CrMath,
226 D: Dimension,
227{
228 unary_float_op_compute(input, T::cr_acos)
229}
230
231pub fn arctan<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
233where
234 T: Element + Float + CrMath,
235 D: Dimension,
236{
237 unary_float_op_compute(input, T::cr_atan)
238}
239
240pub fn arctan2<T, D>(y: &Array<T, D>, x: &Array<T, D>) -> FerrayResult<Array<T, D>>
242where
243 T: Element + Float + CrMath,
244 D: Dimension,
245{
246 binary_elementwise_op(y, x, T::cr_atan2)
247}
248
249pub fn hypot<T, D>(a: &Array<T, D>, b: &Array<T, D>) -> FerrayResult<Array<T, D>>
251where
252 T: Element + Float + CrMath,
253 D: Dimension,
254{
255 binary_elementwise_op(a, b, T::cr_hypot)
256}
257
258pub fn sinh<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
264where
265 T: Element + Float + CrMath,
266 D: Dimension,
267{
268 unary_float_op_compute(input, T::cr_sinh)
269}
270
271pub fn cosh<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
273where
274 T: Element + Float + CrMath,
275 D: Dimension,
276{
277 unary_float_op_compute(input, T::cr_cosh)
278}
279
280pub fn tanh<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
282where
283 T: Element + Float + CrMath,
284 D: Dimension,
285{
286 unary_float_op_compute(input, T::cr_tanh)
287}
288
289pub fn arcsinh<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
291where
292 T: Element + Float + CrMath,
293 D: Dimension,
294{
295 unary_float_op_compute(input, T::cr_asinh)
296}
297
298pub fn arccosh<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
300where
301 T: Element + Float + CrMath,
302 D: Dimension,
303{
304 unary_float_op_compute(input, T::cr_acosh)
305}
306
307pub fn arctanh<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
309where
310 T: Element + Float + CrMath,
311 D: Dimension,
312{
313 unary_float_op_compute(input, T::cr_atanh)
314}
315
316pub fn degrees<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
322where
323 T: Element + Float,
324 D: Dimension,
325{
326 unary_float_op(input, T::to_degrees)
327}
328
329pub fn radians<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
331where
332 T: Element + Float,
333 D: Dimension,
334{
335 unary_float_op(input, T::to_radians)
336}
337
338pub fn deg2rad<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
340where
341 T: Element + Float,
342 D: Dimension,
343{
344 radians(input)
345}
346
347pub fn rad2deg<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
349where
350 T: Element + Float,
351 D: Dimension,
352{
353 degrees(input)
354}
355
356pub fn unwrap<T, D>(input: &Array<T, D>, discont: Option<T>) -> FerrayResult<Array<T, D>>
360where
361 T: Element + Float,
362 D: Dimension,
363{
364 let pi = T::from(std::f64::consts::PI).unwrap_or_else(<T as Element>::zero);
365 let two_pi = pi + pi;
366 let discont = discont.unwrap_or(pi);
367
368 let data: Vec<T> = input.iter().copied().collect();
369 if data.is_empty() {
370 return Array::from_vec(input.dim().clone(), data);
371 }
372
373 let mut result = Vec::with_capacity(data.len());
374 result.push(data[0]);
375 let mut cumulative = <T as Element>::zero();
376
377 for i in 1..data.len() {
378 let mut diff = data[i] - data[i - 1];
379 if diff > discont || diff < -discont {
380 diff = diff - two_pi * ((diff + pi) / two_pi).floor();
381 }
382 cumulative = cumulative + diff - (data[i] - data[i - 1]);
383 result.push(data[i] + cumulative);
384 }
385
386 Array::from_vec(input.dim().clone(), result)
387}
388
389use crate::helpers::{binary_f16_fn, unary_f16_fn};
397
398unary_f16_fn!(
399 #[cfg(feature = "f16")]
401 sin_f16,
402 f32::sin
403);
404unary_f16_fn!(
405 #[cfg(feature = "f16")]
407 cos_f16,
408 f32::cos
409);
410unary_f16_fn!(
411 #[cfg(feature = "f16")]
413 tan_f16,
414 f32::tan
415);
416unary_f16_fn!(
417 #[cfg(feature = "f16")]
419 arcsin_f16,
420 f32::asin
421);
422unary_f16_fn!(
423 #[cfg(feature = "f16")]
425 arccos_f16,
426 f32::acos
427);
428unary_f16_fn!(
429 #[cfg(feature = "f16")]
431 arctan_f16,
432 f32::atan
433);
434binary_f16_fn!(
435 #[cfg(feature = "f16")]
437 arctan2_f16,
438 f32::atan2
439);
440binary_f16_fn!(
441 #[cfg(feature = "f16")]
443 hypot_f16,
444 f32::hypot
445);
446unary_f16_fn!(
447 #[cfg(feature = "f16")]
449 sinh_f16,
450 f32::sinh
451);
452unary_f16_fn!(
453 #[cfg(feature = "f16")]
455 cosh_f16,
456 f32::cosh
457);
458unary_f16_fn!(
459 #[cfg(feature = "f16")]
461 tanh_f16,
462 f32::tanh
463);
464unary_f16_fn!(
465 #[cfg(feature = "f16")]
467 arcsinh_f16,
468 f32::asinh
469);
470unary_f16_fn!(
471 #[cfg(feature = "f16")]
473 arccosh_f16,
474 f32::acosh
475);
476unary_f16_fn!(
477 #[cfg(feature = "f16")]
479 arctanh_f16,
480 f32::atanh
481);
482unary_f16_fn!(
483 #[cfg(feature = "f16")]
485 degrees_f16,
486 f32::to_degrees
487);
488unary_f16_fn!(
489 #[cfg(feature = "f16")]
491 radians_f16,
492 f32::to_radians
493);
494
495#[cfg(test)]
496mod tests {
497 use super::*;
498
499 use crate::test_util::arr1;
500
501 #[test]
502 fn test_sin() {
503 let a = arr1(vec![0.0, std::f64::consts::FRAC_PI_2, std::f64::consts::PI]);
504 let r = sin(&a).unwrap();
505 let s = r.as_slice().unwrap();
506 assert!((s[0]).abs() < 1e-12);
507 assert!((s[1] - 1.0).abs() < 1e-12);
508 assert!((s[2]).abs() < 1e-12);
509 }
510
511 #[test]
512 fn test_cos() {
513 let a = arr1(vec![0.0, std::f64::consts::PI]);
514 let r = cos(&a).unwrap();
515 let s = r.as_slice().unwrap();
516 assert!((s[0] - 1.0).abs() < 1e-12);
517 assert!((s[1] + 1.0).abs() < 1e-12);
518 }
519
520 #[test]
521 fn test_tan() {
522 let a = arr1(vec![0.0, std::f64::consts::FRAC_PI_4]);
523 let r = tan(&a).unwrap();
524 let s = r.as_slice().unwrap();
525 assert!((s[0]).abs() < 1e-12);
526 assert!((s[1] - 1.0).abs() < 1e-12);
527 }
528
529 #[test]
530 fn test_arcsin() {
531 let a = arr1(vec![0.0, 1.0]);
532 let r = arcsin(&a).unwrap();
533 let s = r.as_slice().unwrap();
534 assert!((s[0]).abs() < 1e-12);
535 assert!((s[1] - std::f64::consts::FRAC_PI_2).abs() < 1e-12);
536 }
537
538 #[test]
539 fn test_arctan2() {
540 let y = arr1(vec![0.0, 1.0]);
541 let x = arr1(vec![1.0, 0.0]);
542 let r = arctan2(&y, &x).unwrap();
543 let s = r.as_slice().unwrap();
544 assert!((s[0]).abs() < 1e-12);
545 assert!((s[1] - std::f64::consts::FRAC_PI_2).abs() < 1e-12);
546 }
547
548 #[test]
549 fn test_hypot() {
550 let a = arr1(vec![3.0, 5.0]);
551 let b = arr1(vec![4.0, 12.0]);
552 let r = hypot(&a, &b).unwrap();
553 let s = r.as_slice().unwrap();
554 assert!((s[0] - 5.0).abs() < 1e-12);
555 assert!((s[1] - 13.0).abs() < 1e-12);
556 }
557
558 #[test]
559 fn test_sinh_cosh_tanh() {
560 let a = arr1(vec![0.0]);
561 assert!((sinh(&a).unwrap().as_slice().unwrap()[0]).abs() < 1e-12);
562 assert!((cosh(&a).unwrap().as_slice().unwrap()[0] - 1.0).abs() < 1e-12);
563 assert!((tanh(&a).unwrap().as_slice().unwrap()[0]).abs() < 1e-12);
564 }
565
566 #[test]
567 fn test_degrees_radians() {
568 let a = arr1(vec![std::f64::consts::PI]);
569 let deg = degrees(&a).unwrap();
570 assert!((deg.as_slice().unwrap()[0] - 180.0).abs() < 1e-10);
571
572 let back = radians(°).unwrap();
573 assert!((back.as_slice().unwrap()[0] - std::f64::consts::PI).abs() < 1e-12);
574 }
575
576 #[test]
577 fn test_deg2rad_rad2deg() {
578 let a = arr1(vec![180.0]);
579 let r = deg2rad(&a).unwrap();
580 assert!((r.as_slice().unwrap()[0] - std::f64::consts::PI).abs() < 1e-12);
581
582 let d = rad2deg(&arr1(vec![std::f64::consts::PI])).unwrap();
583 assert!((d.as_slice().unwrap()[0] - 180.0).abs() < 1e-10);
584 }
585
586 #[test]
587 fn test_unwrap_basic() {
588 let a = arr1(vec![0.0, 0.5, 1.0, -0.5, -1.0]);
589 let r = unwrap(&a, None).unwrap();
590 let s = r.as_slice().unwrap();
592 for (i, &v) in s.iter().enumerate() {
593 assert!((v - a.as_slice().unwrap()[i]).abs() < 1e-12);
594 }
595 }
596
597 #[test]
598 fn test_arcsinh_arccosh_arctanh() {
599 let a = arr1(vec![0.0]);
600 assert!((arcsinh(&a).unwrap().as_slice().unwrap()[0]).abs() < 1e-12);
601
602 let b = arr1(vec![1.0]);
603 assert!((arccosh(&b).unwrap().as_slice().unwrap()[0]).abs() < 1e-12);
604 assert!((arctanh(&a).unwrap().as_slice().unwrap()[0]).abs() < 1e-12);
605 }
606
607 #[cfg(feature = "f16")]
608 mod f16_tests {
609 use super::*;
610 use ferray_core::dimension::Ix1;
611
612 fn arr1_f16(data: &[f32]) -> Array<half::f16, Ix1> {
613 let n = data.len();
614 let vals: Vec<half::f16> = data.iter().map(|&x| half::f16::from_f32(x)).collect();
615 Array::from_vec(Ix1::new([n]), vals).unwrap()
616 }
617
618 #[test]
619 fn test_sin_f16() {
620 let a = arr1_f16(&[0.0, std::f32::consts::FRAC_PI_2, std::f32::consts::PI]);
621 let r = sin_f16(&a).unwrap();
622 let s = r.as_slice().unwrap();
623 assert!(s[0].to_f32().abs() < 0.01);
624 assert!((s[1].to_f32() - 1.0).abs() < 0.01);
625 assert!(s[2].to_f32().abs() < 0.01);
626 }
627
628 #[test]
629 fn test_cos_f16() {
630 let a = arr1_f16(&[0.0, std::f32::consts::PI]);
631 let r = cos_f16(&a).unwrap();
632 let s = r.as_slice().unwrap();
633 assert!((s[0].to_f32() - 1.0).abs() < 0.01);
634 assert!((s[1].to_f32() + 1.0).abs() < 0.01);
635 }
636
637 #[test]
638 fn test_tan_f16() {
639 let a = arr1_f16(&[0.0, std::f32::consts::FRAC_PI_4]);
640 let r = tan_f16(&a).unwrap();
641 let s = r.as_slice().unwrap();
642 assert!(s[0].to_f32().abs() < 0.01);
643 assert!((s[1].to_f32() - 1.0).abs() < 0.01);
644 }
645
646 #[test]
647 fn test_arctan2_f16() {
648 let y = arr1_f16(&[0.0, 1.0]);
649 let x = arr1_f16(&[1.0, 0.0]);
650 let r = arctan2_f16(&y, &x).unwrap();
651 let s = r.as_slice().unwrap();
652 assert!(s[0].to_f32().abs() < 0.01);
653 assert!((s[1].to_f32() - std::f32::consts::FRAC_PI_2).abs() < 0.01);
654 }
655
656 #[test]
657 fn test_degrees_radians_f16() {
658 let a = arr1_f16(&[std::f32::consts::PI]);
659 let deg = degrees_f16(&a).unwrap();
660 assert!((deg.as_slice().unwrap()[0].to_f32() - 180.0).abs() < 0.5);
661
662 let back = radians_f16(°).unwrap();
663 assert!((back.as_slice().unwrap()[0].to_f32() - std::f32::consts::PI).abs() < 0.01);
664 }
665 }
666}