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::{binary_float_op, unary_float_op};
15
16pub fn sin<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
22where
23 T: Element + Float + CrMath,
24 D: Dimension,
25{
26 unary_float_op(input, T::cr_sin)
27}
28
29pub fn cos<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
31where
32 T: Element + Float + CrMath,
33 D: Dimension,
34{
35 unary_float_op(input, T::cr_cos)
36}
37
38pub fn tan<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
40where
41 T: Element + Float + CrMath,
42 D: Dimension,
43{
44 unary_float_op(input, T::cr_tan)
45}
46
47pub fn arcsin<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
49where
50 T: Element + Float + CrMath,
51 D: Dimension,
52{
53 unary_float_op(input, T::cr_asin)
54}
55
56pub fn arccos<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
58where
59 T: Element + Float + CrMath,
60 D: Dimension,
61{
62 unary_float_op(input, T::cr_acos)
63}
64
65pub fn arctan<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
67where
68 T: Element + Float + CrMath,
69 D: Dimension,
70{
71 unary_float_op(input, T::cr_atan)
72}
73
74pub fn arctan2<T, D>(y: &Array<T, D>, x: &Array<T, D>) -> FerrayResult<Array<T, D>>
76where
77 T: Element + Float + CrMath,
78 D: Dimension,
79{
80 binary_float_op(y, x, T::cr_atan2)
81}
82
83pub fn hypot<T, D>(a: &Array<T, D>, b: &Array<T, D>) -> FerrayResult<Array<T, D>>
85where
86 T: Element + Float + CrMath,
87 D: Dimension,
88{
89 binary_float_op(a, b, T::cr_hypot)
90}
91
92pub fn sinh<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
98where
99 T: Element + Float + CrMath,
100 D: Dimension,
101{
102 unary_float_op(input, T::cr_sinh)
103}
104
105pub fn cosh<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
107where
108 T: Element + Float + CrMath,
109 D: Dimension,
110{
111 unary_float_op(input, T::cr_cosh)
112}
113
114pub fn tanh<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
116where
117 T: Element + Float + CrMath,
118 D: Dimension,
119{
120 unary_float_op(input, T::cr_tanh)
121}
122
123pub fn arcsinh<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
125where
126 T: Element + Float + CrMath,
127 D: Dimension,
128{
129 unary_float_op(input, T::cr_asinh)
130}
131
132pub fn arccosh<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
134where
135 T: Element + Float + CrMath,
136 D: Dimension,
137{
138 unary_float_op(input, T::cr_acosh)
139}
140
141pub fn arctanh<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
143where
144 T: Element + Float + CrMath,
145 D: Dimension,
146{
147 unary_float_op(input, T::cr_atanh)
148}
149
150pub fn degrees<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
156where
157 T: Element + Float,
158 D: Dimension,
159{
160 unary_float_op(input, T::to_degrees)
161}
162
163pub fn radians<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
165where
166 T: Element + Float,
167 D: Dimension,
168{
169 unary_float_op(input, T::to_radians)
170}
171
172pub fn deg2rad<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
174where
175 T: Element + Float,
176 D: Dimension,
177{
178 radians(input)
179}
180
181pub fn rad2deg<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
183where
184 T: Element + Float,
185 D: Dimension,
186{
187 degrees(input)
188}
189
190pub fn unwrap<T, D>(input: &Array<T, D>, discont: Option<T>) -> FerrayResult<Array<T, D>>
194where
195 T: Element + Float,
196 D: Dimension,
197{
198 let pi = T::from(std::f64::consts::PI).unwrap_or_else(<T as Element>::zero);
199 let two_pi = pi + pi;
200 let discont = discont.unwrap_or(pi);
201
202 let data: Vec<T> = input.iter().copied().collect();
203 if data.is_empty() {
204 return Array::from_vec(input.dim().clone(), data);
205 }
206
207 let mut result = Vec::with_capacity(data.len());
208 result.push(data[0]);
209 let mut cumulative = <T as Element>::zero();
210
211 for i in 1..data.len() {
212 let mut diff = data[i] - data[i - 1];
213 if diff > discont {
214 while diff > pi {
215 diff = diff - two_pi;
216 }
217 } else if diff < -discont {
218 while diff < -pi {
219 diff = diff + two_pi;
220 }
221 }
222 cumulative = cumulative + diff - (data[i] - data[i - 1]);
223 result.push(data[i] + cumulative);
224 }
225
226 Array::from_vec(input.dim().clone(), result)
227}
228
229#[cfg(feature = "f16")]
235pub fn sin_f16<D>(input: &Array<half::f16, D>) -> FerrayResult<Array<half::f16, D>>
236where
237 D: Dimension,
238{
239 crate::helpers::unary_f16_op(input, f32::sin)
240}
241
242#[cfg(feature = "f16")]
244pub fn cos_f16<D>(input: &Array<half::f16, D>) -> FerrayResult<Array<half::f16, D>>
245where
246 D: Dimension,
247{
248 crate::helpers::unary_f16_op(input, f32::cos)
249}
250
251#[cfg(feature = "f16")]
253pub fn tan_f16<D>(input: &Array<half::f16, D>) -> FerrayResult<Array<half::f16, D>>
254where
255 D: Dimension,
256{
257 crate::helpers::unary_f16_op(input, f32::tan)
258}
259
260#[cfg(feature = "f16")]
262pub fn arcsin_f16<D>(input: &Array<half::f16, D>) -> FerrayResult<Array<half::f16, D>>
263where
264 D: Dimension,
265{
266 crate::helpers::unary_f16_op(input, f32::asin)
267}
268
269#[cfg(feature = "f16")]
271pub fn arccos_f16<D>(input: &Array<half::f16, D>) -> FerrayResult<Array<half::f16, D>>
272where
273 D: Dimension,
274{
275 crate::helpers::unary_f16_op(input, f32::acos)
276}
277
278#[cfg(feature = "f16")]
280pub fn arctan_f16<D>(input: &Array<half::f16, D>) -> FerrayResult<Array<half::f16, D>>
281where
282 D: Dimension,
283{
284 crate::helpers::unary_f16_op(input, f32::atan)
285}
286
287#[cfg(feature = "f16")]
289pub fn arctan2_f16<D>(
290 y: &Array<half::f16, D>,
291 x: &Array<half::f16, D>,
292) -> FerrayResult<Array<half::f16, D>>
293where
294 D: Dimension,
295{
296 crate::helpers::binary_f16_op(y, x, f32::atan2)
297}
298
299#[cfg(feature = "f16")]
301pub fn hypot_f16<D>(
302 a: &Array<half::f16, D>,
303 b: &Array<half::f16, D>,
304) -> FerrayResult<Array<half::f16, D>>
305where
306 D: Dimension,
307{
308 crate::helpers::binary_f16_op(a, b, f32::hypot)
309}
310
311#[cfg(feature = "f16")]
313pub fn sinh_f16<D>(input: &Array<half::f16, D>) -> FerrayResult<Array<half::f16, D>>
314where
315 D: Dimension,
316{
317 crate::helpers::unary_f16_op(input, f32::sinh)
318}
319
320#[cfg(feature = "f16")]
322pub fn cosh_f16<D>(input: &Array<half::f16, D>) -> FerrayResult<Array<half::f16, D>>
323where
324 D: Dimension,
325{
326 crate::helpers::unary_f16_op(input, f32::cosh)
327}
328
329#[cfg(feature = "f16")]
331pub fn tanh_f16<D>(input: &Array<half::f16, D>) -> FerrayResult<Array<half::f16, D>>
332where
333 D: Dimension,
334{
335 crate::helpers::unary_f16_op(input, f32::tanh)
336}
337
338#[cfg(feature = "f16")]
340pub fn arcsinh_f16<D>(input: &Array<half::f16, D>) -> FerrayResult<Array<half::f16, D>>
341where
342 D: Dimension,
343{
344 crate::helpers::unary_f16_op(input, f32::asinh)
345}
346
347#[cfg(feature = "f16")]
349pub fn arccosh_f16<D>(input: &Array<half::f16, D>) -> FerrayResult<Array<half::f16, D>>
350where
351 D: Dimension,
352{
353 crate::helpers::unary_f16_op(input, f32::acosh)
354}
355
356#[cfg(feature = "f16")]
358pub fn arctanh_f16<D>(input: &Array<half::f16, D>) -> FerrayResult<Array<half::f16, D>>
359where
360 D: Dimension,
361{
362 crate::helpers::unary_f16_op(input, f32::atanh)
363}
364
365#[cfg(feature = "f16")]
367pub fn degrees_f16<D>(input: &Array<half::f16, D>) -> FerrayResult<Array<half::f16, D>>
368where
369 D: Dimension,
370{
371 crate::helpers::unary_f16_op(input, f32::to_degrees)
372}
373
374#[cfg(feature = "f16")]
376pub fn radians_f16<D>(input: &Array<half::f16, D>) -> FerrayResult<Array<half::f16, D>>
377where
378 D: Dimension,
379{
380 crate::helpers::unary_f16_op(input, f32::to_radians)
381}
382
383#[cfg(test)]
384mod tests {
385 use super::*;
386 use ferray_core::dimension::Ix1;
387
388 fn arr1(data: Vec<f64>) -> Array<f64, Ix1> {
389 let n = data.len();
390 Array::from_vec(Ix1::new([n]), data).unwrap()
391 }
392
393 #[test]
394 fn test_sin() {
395 let a = arr1(vec![0.0, std::f64::consts::FRAC_PI_2, std::f64::consts::PI]);
396 let r = sin(&a).unwrap();
397 let s = r.as_slice().unwrap();
398 assert!((s[0]).abs() < 1e-12);
399 assert!((s[1] - 1.0).abs() < 1e-12);
400 assert!((s[2]).abs() < 1e-12);
401 }
402
403 #[test]
404 fn test_cos() {
405 let a = arr1(vec![0.0, std::f64::consts::PI]);
406 let r = cos(&a).unwrap();
407 let s = r.as_slice().unwrap();
408 assert!((s[0] - 1.0).abs() < 1e-12);
409 assert!((s[1] + 1.0).abs() < 1e-12);
410 }
411
412 #[test]
413 fn test_tan() {
414 let a = arr1(vec![0.0, std::f64::consts::FRAC_PI_4]);
415 let r = tan(&a).unwrap();
416 let s = r.as_slice().unwrap();
417 assert!((s[0]).abs() < 1e-12);
418 assert!((s[1] - 1.0).abs() < 1e-12);
419 }
420
421 #[test]
422 fn test_arcsin() {
423 let a = arr1(vec![0.0, 1.0]);
424 let r = arcsin(&a).unwrap();
425 let s = r.as_slice().unwrap();
426 assert!((s[0]).abs() < 1e-12);
427 assert!((s[1] - std::f64::consts::FRAC_PI_2).abs() < 1e-12);
428 }
429
430 #[test]
431 fn test_arctan2() {
432 let y = arr1(vec![0.0, 1.0]);
433 let x = arr1(vec![1.0, 0.0]);
434 let r = arctan2(&y, &x).unwrap();
435 let s = r.as_slice().unwrap();
436 assert!((s[0]).abs() < 1e-12);
437 assert!((s[1] - std::f64::consts::FRAC_PI_2).abs() < 1e-12);
438 }
439
440 #[test]
441 fn test_hypot() {
442 let a = arr1(vec![3.0, 5.0]);
443 let b = arr1(vec![4.0, 12.0]);
444 let r = hypot(&a, &b).unwrap();
445 let s = r.as_slice().unwrap();
446 assert!((s[0] - 5.0).abs() < 1e-12);
447 assert!((s[1] - 13.0).abs() < 1e-12);
448 }
449
450 #[test]
451 fn test_sinh_cosh_tanh() {
452 let a = arr1(vec![0.0]);
453 assert!((sinh(&a).unwrap().as_slice().unwrap()[0]).abs() < 1e-12);
454 assert!((cosh(&a).unwrap().as_slice().unwrap()[0] - 1.0).abs() < 1e-12);
455 assert!((tanh(&a).unwrap().as_slice().unwrap()[0]).abs() < 1e-12);
456 }
457
458 #[test]
459 fn test_degrees_radians() {
460 let a = arr1(vec![std::f64::consts::PI]);
461 let deg = degrees(&a).unwrap();
462 assert!((deg.as_slice().unwrap()[0] - 180.0).abs() < 1e-10);
463
464 let back = radians(°).unwrap();
465 assert!((back.as_slice().unwrap()[0] - std::f64::consts::PI).abs() < 1e-12);
466 }
467
468 #[test]
469 fn test_deg2rad_rad2deg() {
470 let a = arr1(vec![180.0]);
471 let r = deg2rad(&a).unwrap();
472 assert!((r.as_slice().unwrap()[0] - std::f64::consts::PI).abs() < 1e-12);
473
474 let d = rad2deg(&arr1(vec![std::f64::consts::PI])).unwrap();
475 assert!((d.as_slice().unwrap()[0] - 180.0).abs() < 1e-10);
476 }
477
478 #[test]
479 fn test_unwrap_basic() {
480 let a = arr1(vec![0.0, 0.5, 1.0, -0.5, -1.0]);
481 let r = unwrap(&a, None).unwrap();
482 let s = r.as_slice().unwrap();
484 for (i, &v) in s.iter().enumerate() {
485 assert!((v - a.as_slice().unwrap()[i]).abs() < 1e-12);
486 }
487 }
488
489 #[test]
490 fn test_arcsinh_arccosh_arctanh() {
491 let a = arr1(vec![0.0]);
492 assert!((arcsinh(&a).unwrap().as_slice().unwrap()[0]).abs() < 1e-12);
493
494 let b = arr1(vec![1.0]);
495 assert!((arccosh(&b).unwrap().as_slice().unwrap()[0]).abs() < 1e-12);
496 assert!((arctanh(&a).unwrap().as_slice().unwrap()[0]).abs() < 1e-12);
497 }
498
499 #[cfg(feature = "f16")]
500 mod f16_tests {
501 use super::*;
502
503 fn arr1_f16(data: &[f32]) -> Array<half::f16, Ix1> {
504 let n = data.len();
505 let vals: Vec<half::f16> = data.iter().map(|&x| half::f16::from_f32(x)).collect();
506 Array::from_vec(Ix1::new([n]), vals).unwrap()
507 }
508
509 #[test]
510 fn test_sin_f16() {
511 let a = arr1_f16(&[0.0, std::f32::consts::FRAC_PI_2, std::f32::consts::PI]);
512 let r = sin_f16(&a).unwrap();
513 let s = r.as_slice().unwrap();
514 assert!(s[0].to_f32().abs() < 0.01);
515 assert!((s[1].to_f32() - 1.0).abs() < 0.01);
516 assert!(s[2].to_f32().abs() < 0.01);
517 }
518
519 #[test]
520 fn test_cos_f16() {
521 let a = arr1_f16(&[0.0, std::f32::consts::PI]);
522 let r = cos_f16(&a).unwrap();
523 let s = r.as_slice().unwrap();
524 assert!((s[0].to_f32() - 1.0).abs() < 0.01);
525 assert!((s[1].to_f32() + 1.0).abs() < 0.01);
526 }
527
528 #[test]
529 fn test_tan_f16() {
530 let a = arr1_f16(&[0.0, std::f32::consts::FRAC_PI_4]);
531 let r = tan_f16(&a).unwrap();
532 let s = r.as_slice().unwrap();
533 assert!(s[0].to_f32().abs() < 0.01);
534 assert!((s[1].to_f32() - 1.0).abs() < 0.01);
535 }
536
537 #[test]
538 fn test_arctan2_f16() {
539 let y = arr1_f16(&[0.0, 1.0]);
540 let x = arr1_f16(&[1.0, 0.0]);
541 let r = arctan2_f16(&y, &x).unwrap();
542 let s = r.as_slice().unwrap();
543 assert!(s[0].to_f32().abs() < 0.01);
544 assert!((s[1].to_f32() - std::f32::consts::FRAC_PI_2).abs() < 0.01);
545 }
546
547 #[test]
548 fn test_degrees_radians_f16() {
549 let a = arr1_f16(&[std::f32::consts::PI]);
550 let deg = degrees_f16(&a).unwrap();
551 assert!((deg.as_slice().unwrap()[0].to_f32() - 180.0).abs() < 0.5);
552
553 let back = radians_f16(°).unwrap();
554 assert!((back.as_slice().unwrap()[0].to_f32() - std::f32::consts::PI).abs() < 0.01);
555 }
556 }
557}