1use ferray_core::Array;
8use ferray_core::dimension::Dimension;
9use ferray_core::dtype::Element;
10use ferray_core::error::FerrayResult;
11use num_traits::Float;
12
13use crate::cr_math::CrMath;
14use crate::helpers::{
15 binary_elementwise_op, unary_float_op, unary_float_op_compute, unary_float_op_into_compute,
16};
17
18pub fn sin<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
30where
31 T: Element + Float,
32 D: Dimension,
33{
34 unary_float_op_compute(input, T::sin)
35}
36
37pub fn sin_into<T, D>(input: &Array<T, D>, out: &mut Array<T, D>) -> FerrayResult<()>
39where
40 T: Element + Float,
41 D: Dimension,
42{
43 unary_float_op_into_compute(input, out, "sin", T::sin)
44}
45
46pub fn cos<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
48where
49 T: Element + Float,
50 D: Dimension,
51{
52 unary_float_op_compute(input, T::cos)
53}
54
55pub fn cos_into<T, D>(input: &Array<T, D>, out: &mut Array<T, D>) -> FerrayResult<()>
57where
58 T: Element + Float,
59 D: Dimension,
60{
61 unary_float_op_into_compute(input, out, "cos", T::cos)
62}
63
64pub fn tan<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
66where
67 T: Element + Float,
68 D: Dimension,
69{
70 unary_float_op_compute(input, T::tan)
71}
72
73pub fn arcsin<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
75where
76 T: Element + Float + CrMath,
77 D: Dimension,
78{
79 unary_float_op_compute(input, T::cr_asin)
80}
81
82pub fn arccos<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
84where
85 T: Element + Float + CrMath,
86 D: Dimension,
87{
88 unary_float_op_compute(input, T::cr_acos)
89}
90
91pub fn arctan<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
93where
94 T: Element + Float + CrMath,
95 D: Dimension,
96{
97 unary_float_op_compute(input, T::cr_atan)
98}
99
100pub fn arctan2<T, D>(y: &Array<T, D>, x: &Array<T, D>) -> FerrayResult<Array<T, D>>
102where
103 T: Element + Float + CrMath,
104 D: Dimension,
105{
106 binary_elementwise_op(y, x, T::cr_atan2)
107}
108
109pub fn hypot<T, D>(a: &Array<T, D>, b: &Array<T, D>) -> FerrayResult<Array<T, D>>
111where
112 T: Element + Float + CrMath,
113 D: Dimension,
114{
115 binary_elementwise_op(a, b, T::cr_hypot)
116}
117
118pub fn sinh<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
124where
125 T: Element + Float + CrMath,
126 D: Dimension,
127{
128 unary_float_op_compute(input, T::cr_sinh)
129}
130
131pub fn cosh<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
133where
134 T: Element + Float + CrMath,
135 D: Dimension,
136{
137 unary_float_op_compute(input, T::cr_cosh)
138}
139
140pub fn tanh<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
142where
143 T: Element + Float + CrMath,
144 D: Dimension,
145{
146 unary_float_op_compute(input, T::cr_tanh)
147}
148
149pub fn arcsinh<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
151where
152 T: Element + Float + CrMath,
153 D: Dimension,
154{
155 unary_float_op_compute(input, T::cr_asinh)
156}
157
158pub fn arccosh<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
160where
161 T: Element + Float + CrMath,
162 D: Dimension,
163{
164 unary_float_op_compute(input, T::cr_acosh)
165}
166
167pub fn arctanh<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
169where
170 T: Element + Float + CrMath,
171 D: Dimension,
172{
173 unary_float_op_compute(input, T::cr_atanh)
174}
175
176pub fn degrees<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
182where
183 T: Element + Float,
184 D: Dimension,
185{
186 unary_float_op(input, T::to_degrees)
187}
188
189pub fn radians<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
191where
192 T: Element + Float,
193 D: Dimension,
194{
195 unary_float_op(input, T::to_radians)
196}
197
198pub fn deg2rad<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
200where
201 T: Element + Float,
202 D: Dimension,
203{
204 radians(input)
205}
206
207pub fn rad2deg<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
209where
210 T: Element + Float,
211 D: Dimension,
212{
213 degrees(input)
214}
215
216pub fn unwrap<T, D>(input: &Array<T, D>, discont: Option<T>) -> FerrayResult<Array<T, D>>
220where
221 T: Element + Float,
222 D: Dimension,
223{
224 let pi = T::from(std::f64::consts::PI).unwrap_or_else(<T as Element>::zero);
225 let two_pi = pi + pi;
226 let discont = discont.unwrap_or(pi);
227
228 let data: Vec<T> = input.iter().copied().collect();
229 if data.is_empty() {
230 return Array::from_vec(input.dim().clone(), data);
231 }
232
233 let mut result = Vec::with_capacity(data.len());
234 result.push(data[0]);
235 let mut cumulative = <T as Element>::zero();
236
237 for i in 1..data.len() {
238 let mut diff = data[i] - data[i - 1];
239 if diff > discont || diff < -discont {
240 diff = diff - two_pi * ((diff + pi) / two_pi).floor();
241 }
242 cumulative = cumulative + diff - (data[i] - data[i - 1]);
243 result.push(data[i] + cumulative);
244 }
245
246 Array::from_vec(input.dim().clone(), result)
247}
248
249use crate::helpers::{binary_f16_fn, unary_f16_fn};
257
258unary_f16_fn!(
259 #[cfg(feature = "f16")]
261 sin_f16,
262 f32::sin
263);
264unary_f16_fn!(
265 #[cfg(feature = "f16")]
267 cos_f16,
268 f32::cos
269);
270unary_f16_fn!(
271 #[cfg(feature = "f16")]
273 tan_f16,
274 f32::tan
275);
276unary_f16_fn!(
277 #[cfg(feature = "f16")]
279 arcsin_f16,
280 f32::asin
281);
282unary_f16_fn!(
283 #[cfg(feature = "f16")]
285 arccos_f16,
286 f32::acos
287);
288unary_f16_fn!(
289 #[cfg(feature = "f16")]
291 arctan_f16,
292 f32::atan
293);
294binary_f16_fn!(
295 #[cfg(feature = "f16")]
297 arctan2_f16,
298 f32::atan2
299);
300binary_f16_fn!(
301 #[cfg(feature = "f16")]
303 hypot_f16,
304 f32::hypot
305);
306unary_f16_fn!(
307 #[cfg(feature = "f16")]
309 sinh_f16,
310 f32::sinh
311);
312unary_f16_fn!(
313 #[cfg(feature = "f16")]
315 cosh_f16,
316 f32::cosh
317);
318unary_f16_fn!(
319 #[cfg(feature = "f16")]
321 tanh_f16,
322 f32::tanh
323);
324unary_f16_fn!(
325 #[cfg(feature = "f16")]
327 arcsinh_f16,
328 f32::asinh
329);
330unary_f16_fn!(
331 #[cfg(feature = "f16")]
333 arccosh_f16,
334 f32::acosh
335);
336unary_f16_fn!(
337 #[cfg(feature = "f16")]
339 arctanh_f16,
340 f32::atanh
341);
342unary_f16_fn!(
343 #[cfg(feature = "f16")]
345 degrees_f16,
346 f32::to_degrees
347);
348unary_f16_fn!(
349 #[cfg(feature = "f16")]
351 radians_f16,
352 f32::to_radians
353);
354
355#[cfg(test)]
356mod tests {
357 use super::*;
358
359 use crate::test_util::arr1;
360
361 #[test]
362 fn test_sin() {
363 let a = arr1(vec![0.0, std::f64::consts::FRAC_PI_2, std::f64::consts::PI]);
364 let r = sin(&a).unwrap();
365 let s = r.as_slice().unwrap();
366 assert!((s[0]).abs() < 1e-12);
367 assert!((s[1] - 1.0).abs() < 1e-12);
368 assert!((s[2]).abs() < 1e-12);
369 }
370
371 #[test]
372 fn test_cos() {
373 let a = arr1(vec![0.0, std::f64::consts::PI]);
374 let r = cos(&a).unwrap();
375 let s = r.as_slice().unwrap();
376 assert!((s[0] - 1.0).abs() < 1e-12);
377 assert!((s[1] + 1.0).abs() < 1e-12);
378 }
379
380 #[test]
381 fn test_tan() {
382 let a = arr1(vec![0.0, std::f64::consts::FRAC_PI_4]);
383 let r = tan(&a).unwrap();
384 let s = r.as_slice().unwrap();
385 assert!((s[0]).abs() < 1e-12);
386 assert!((s[1] - 1.0).abs() < 1e-12);
387 }
388
389 #[test]
390 fn test_arcsin() {
391 let a = arr1(vec![0.0, 1.0]);
392 let r = arcsin(&a).unwrap();
393 let s = r.as_slice().unwrap();
394 assert!((s[0]).abs() < 1e-12);
395 assert!((s[1] - std::f64::consts::FRAC_PI_2).abs() < 1e-12);
396 }
397
398 #[test]
399 fn test_arctan2() {
400 let y = arr1(vec![0.0, 1.0]);
401 let x = arr1(vec![1.0, 0.0]);
402 let r = arctan2(&y, &x).unwrap();
403 let s = r.as_slice().unwrap();
404 assert!((s[0]).abs() < 1e-12);
405 assert!((s[1] - std::f64::consts::FRAC_PI_2).abs() < 1e-12);
406 }
407
408 #[test]
409 fn test_hypot() {
410 let a = arr1(vec![3.0, 5.0]);
411 let b = arr1(vec![4.0, 12.0]);
412 let r = hypot(&a, &b).unwrap();
413 let s = r.as_slice().unwrap();
414 assert!((s[0] - 5.0).abs() < 1e-12);
415 assert!((s[1] - 13.0).abs() < 1e-12);
416 }
417
418 #[test]
419 fn test_sinh_cosh_tanh() {
420 let a = arr1(vec![0.0]);
421 assert!((sinh(&a).unwrap().as_slice().unwrap()[0]).abs() < 1e-12);
422 assert!((cosh(&a).unwrap().as_slice().unwrap()[0] - 1.0).abs() < 1e-12);
423 assert!((tanh(&a).unwrap().as_slice().unwrap()[0]).abs() < 1e-12);
424 }
425
426 #[test]
427 fn test_degrees_radians() {
428 let a = arr1(vec![std::f64::consts::PI]);
429 let deg = degrees(&a).unwrap();
430 assert!((deg.as_slice().unwrap()[0] - 180.0).abs() < 1e-10);
431
432 let back = radians(°).unwrap();
433 assert!((back.as_slice().unwrap()[0] - std::f64::consts::PI).abs() < 1e-12);
434 }
435
436 #[test]
437 fn test_deg2rad_rad2deg() {
438 let a = arr1(vec![180.0]);
439 let r = deg2rad(&a).unwrap();
440 assert!((r.as_slice().unwrap()[0] - std::f64::consts::PI).abs() < 1e-12);
441
442 let d = rad2deg(&arr1(vec![std::f64::consts::PI])).unwrap();
443 assert!((d.as_slice().unwrap()[0] - 180.0).abs() < 1e-10);
444 }
445
446 #[test]
447 fn test_unwrap_basic() {
448 let a = arr1(vec![0.0, 0.5, 1.0, -0.5, -1.0]);
449 let r = unwrap(&a, None).unwrap();
450 let s = r.as_slice().unwrap();
452 for (i, &v) in s.iter().enumerate() {
453 assert!((v - a.as_slice().unwrap()[i]).abs() < 1e-12);
454 }
455 }
456
457 #[test]
458 fn test_arcsinh_arccosh_arctanh() {
459 let a = arr1(vec![0.0]);
460 assert!((arcsinh(&a).unwrap().as_slice().unwrap()[0]).abs() < 1e-12);
461
462 let b = arr1(vec![1.0]);
463 assert!((arccosh(&b).unwrap().as_slice().unwrap()[0]).abs() < 1e-12);
464 assert!((arctanh(&a).unwrap().as_slice().unwrap()[0]).abs() < 1e-12);
465 }
466
467 #[cfg(feature = "f16")]
468 mod f16_tests {
469 use super::*;
470 use ferray_core::dimension::Ix1;
471
472 fn arr1_f16(data: &[f32]) -> Array<half::f16, Ix1> {
473 let n = data.len();
474 let vals: Vec<half::f16> = data.iter().map(|&x| half::f16::from_f32(x)).collect();
475 Array::from_vec(Ix1::new([n]), vals).unwrap()
476 }
477
478 #[test]
479 fn test_sin_f16() {
480 let a = arr1_f16(&[0.0, std::f32::consts::FRAC_PI_2, std::f32::consts::PI]);
481 let r = sin_f16(&a).unwrap();
482 let s = r.as_slice().unwrap();
483 assert!(s[0].to_f32().abs() < 0.01);
484 assert!((s[1].to_f32() - 1.0).abs() < 0.01);
485 assert!(s[2].to_f32().abs() < 0.01);
486 }
487
488 #[test]
489 fn test_cos_f16() {
490 let a = arr1_f16(&[0.0, std::f32::consts::PI]);
491 let r = cos_f16(&a).unwrap();
492 let s = r.as_slice().unwrap();
493 assert!((s[0].to_f32() - 1.0).abs() < 0.01);
494 assert!((s[1].to_f32() + 1.0).abs() < 0.01);
495 }
496
497 #[test]
498 fn test_tan_f16() {
499 let a = arr1_f16(&[0.0, std::f32::consts::FRAC_PI_4]);
500 let r = tan_f16(&a).unwrap();
501 let s = r.as_slice().unwrap();
502 assert!(s[0].to_f32().abs() < 0.01);
503 assert!((s[1].to_f32() - 1.0).abs() < 0.01);
504 }
505
506 #[test]
507 fn test_arctan2_f16() {
508 let y = arr1_f16(&[0.0, 1.0]);
509 let x = arr1_f16(&[1.0, 0.0]);
510 let r = arctan2_f16(&y, &x).unwrap();
511 let s = r.as_slice().unwrap();
512 assert!(s[0].to_f32().abs() < 0.01);
513 assert!((s[1].to_f32() - std::f32::consts::FRAC_PI_2).abs() < 0.01);
514 }
515
516 #[test]
517 fn test_degrees_radians_f16() {
518 let a = arr1_f16(&[std::f32::consts::PI]);
519 let deg = degrees_f16(&a).unwrap();
520 assert!((deg.as_slice().unwrap()[0].to_f32() - 180.0).abs() < 0.5);
521
522 let back = radians_f16(°).unwrap();
523 assert!((back.as_slice().unwrap()[0].to_f32() - std::f32::consts::PI).abs() < 0.01);
524 }
525 }
526}