use ferray_core::Array;
use ferray_core::dimension::Dimension;
use ferray_core::dtype::Element;
use ferray_core::error::FerrayResult;
use num_traits::Float;
use crate::cr_math::CrMath;
use crate::helpers::{
binary_elementwise_op, unary_float_op, unary_float_op_compute, unary_float_op_into_compute,
};
pub fn sin<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
where
T: Element + Float,
D: Dimension,
{
unary_float_op_compute(input, T::sin)
}
pub fn sin_into<T, D>(input: &Array<T, D>, out: &mut Array<T, D>) -> FerrayResult<()>
where
T: Element + Float,
D: Dimension,
{
unary_float_op_into_compute(input, out, "sin", T::sin)
}
pub fn cos<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
where
T: Element + Float,
D: Dimension,
{
unary_float_op_compute(input, T::cos)
}
pub fn cos_into<T, D>(input: &Array<T, D>, out: &mut Array<T, D>) -> FerrayResult<()>
where
T: Element + Float,
D: Dimension,
{
unary_float_op_into_compute(input, out, "cos", T::cos)
}
pub fn sin_fast<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
where
T: Element + Float,
D: Dimension,
{
use std::any::TypeId;
if TypeId::of::<T>() == TypeId::of::<f64>() {
let f64_input =
unsafe { &*std::ptr::from_ref::<Array<T, D>>(input).cast::<Array<f64, D>>() };
let n = f64_input.size();
let result = if let Some(slice) = f64_input.as_slice() {
let mut data = vec![0.0_f64; n];
crate::fast_trig::sin_fast_batch_f64(slice, &mut data);
Array::from_vec(f64_input.dim().clone(), data)?
} else {
let data: Vec<f64> = f64_input
.iter()
.map(|&x| crate::fast_trig::sin_fast_f64(x))
.collect();
Array::from_vec(f64_input.dim().clone(), data)?
};
Ok(unsafe { crate::helpers::reinterpret_array::<f64, T, D>(result) })
} else if TypeId::of::<T>() == TypeId::of::<f32>() {
let f32_input =
unsafe { &*std::ptr::from_ref::<Array<T, D>>(input).cast::<Array<f32, D>>() };
let n = f32_input.size();
let result = if let Some(slice) = f32_input.as_slice() {
let mut data = vec![0.0_f32; n];
crate::fast_trig::sin_fast_batch_f32(slice, &mut data);
Array::from_vec(f32_input.dim().clone(), data)?
} else {
let data: Vec<f32> = f32_input
.iter()
.map(|&x| crate::fast_trig::sin_fast_f32(x))
.collect();
Array::from_vec(f32_input.dim().clone(), data)?
};
Ok(unsafe { crate::helpers::reinterpret_array::<f32, T, D>(result) })
} else {
unary_float_op_compute(input, T::sin)
}
}
pub fn cos_fast<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
where
T: Element + Float,
D: Dimension,
{
use std::any::TypeId;
if TypeId::of::<T>() == TypeId::of::<f64>() {
let f64_input =
unsafe { &*std::ptr::from_ref::<Array<T, D>>(input).cast::<Array<f64, D>>() };
let n = f64_input.size();
let result = if let Some(slice) = f64_input.as_slice() {
let mut data = vec![0.0_f64; n];
crate::fast_trig::cos_fast_batch_f64(slice, &mut data);
Array::from_vec(f64_input.dim().clone(), data)?
} else {
let data: Vec<f64> = f64_input
.iter()
.map(|&x| crate::fast_trig::cos_fast_f64(x))
.collect();
Array::from_vec(f64_input.dim().clone(), data)?
};
Ok(unsafe { crate::helpers::reinterpret_array::<f64, T, D>(result) })
} else if TypeId::of::<T>() == TypeId::of::<f32>() {
let f32_input =
unsafe { &*std::ptr::from_ref::<Array<T, D>>(input).cast::<Array<f32, D>>() };
let n = f32_input.size();
let result = if let Some(slice) = f32_input.as_slice() {
let mut data = vec![0.0_f32; n];
crate::fast_trig::cos_fast_batch_f32(slice, &mut data);
Array::from_vec(f32_input.dim().clone(), data)?
} else {
let data: Vec<f32> = f32_input
.iter()
.map(|&x| crate::fast_trig::cos_fast_f32(x))
.collect();
Array::from_vec(f32_input.dim().clone(), data)?
};
Ok(unsafe { crate::helpers::reinterpret_array::<f32, T, D>(result) })
} else {
unary_float_op_compute(input, T::cos)
}
}
pub fn tan<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
where
T: Element + Float,
D: Dimension,
{
unary_float_op_compute(input, T::tan)
}
pub fn arcsin<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
where
T: Element + Float + CrMath,
D: Dimension,
{
unary_float_op_compute(input, T::cr_asin)
}
pub fn arccos<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
where
T: Element + Float + CrMath,
D: Dimension,
{
unary_float_op_compute(input, T::cr_acos)
}
pub fn arctan<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
where
T: Element + Float + CrMath,
D: Dimension,
{
unary_float_op_compute(input, T::cr_atan)
}
pub fn arctan2<T, D>(y: &Array<T, D>, x: &Array<T, D>) -> FerrayResult<Array<T, D>>
where
T: Element + Float + CrMath,
D: Dimension,
{
binary_elementwise_op(y, x, T::cr_atan2)
}
pub fn hypot<T, D>(a: &Array<T, D>, b: &Array<T, D>) -> FerrayResult<Array<T, D>>
where
T: Element + Float + CrMath,
D: Dimension,
{
binary_elementwise_op(a, b, T::cr_hypot)
}
pub fn sinh<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
where
T: Element + Float + CrMath,
D: Dimension,
{
unary_float_op_compute(input, T::cr_sinh)
}
pub fn cosh<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
where
T: Element + Float + CrMath,
D: Dimension,
{
unary_float_op_compute(input, T::cr_cosh)
}
pub fn tanh<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
where
T: Element + Float + CrMath,
D: Dimension,
{
unary_float_op_compute(input, T::cr_tanh)
}
pub fn arcsinh<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
where
T: Element + Float + CrMath,
D: Dimension,
{
unary_float_op_compute(input, T::cr_asinh)
}
pub fn arccosh<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
where
T: Element + Float + CrMath,
D: Dimension,
{
unary_float_op_compute(input, T::cr_acosh)
}
pub fn arctanh<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
where
T: Element + Float + CrMath,
D: Dimension,
{
unary_float_op_compute(input, T::cr_atanh)
}
pub fn degrees<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
where
T: Element + Float,
D: Dimension,
{
unary_float_op(input, T::to_degrees)
}
pub fn radians<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
where
T: Element + Float,
D: Dimension,
{
unary_float_op(input, T::to_radians)
}
pub fn deg2rad<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
where
T: Element + Float,
D: Dimension,
{
radians(input)
}
pub fn rad2deg<T, D>(input: &Array<T, D>) -> FerrayResult<Array<T, D>>
where
T: Element + Float,
D: Dimension,
{
degrees(input)
}
pub fn unwrap<T, D>(input: &Array<T, D>, discont: Option<T>) -> FerrayResult<Array<T, D>>
where
T: Element + Float,
D: Dimension,
{
let pi = T::from(std::f64::consts::PI).unwrap_or_else(<T as Element>::zero);
let two_pi = pi + pi;
let discont = discont.unwrap_or(pi);
let data: Vec<T> = input.iter().copied().collect();
if data.is_empty() {
return Array::from_vec(input.dim().clone(), data);
}
let mut result = Vec::with_capacity(data.len());
result.push(data[0]);
let mut cumulative = <T as Element>::zero();
for i in 1..data.len() {
let mut diff = data[i] - data[i - 1];
if diff > discont || diff < -discont {
diff = diff - two_pi * ((diff + pi) / two_pi).floor();
}
cumulative = cumulative + diff - (data[i] - data[i - 1]);
result.push(data[i] + cumulative);
}
Array::from_vec(input.dim().clone(), result)
}
use crate::helpers::{binary_f16_fn, unary_f16_fn};
unary_f16_fn!(
#[cfg(feature = "f16")]
sin_f16,
f32::sin
);
unary_f16_fn!(
#[cfg(feature = "f16")]
cos_f16,
f32::cos
);
unary_f16_fn!(
#[cfg(feature = "f16")]
tan_f16,
f32::tan
);
unary_f16_fn!(
#[cfg(feature = "f16")]
arcsin_f16,
f32::asin
);
unary_f16_fn!(
#[cfg(feature = "f16")]
arccos_f16,
f32::acos
);
unary_f16_fn!(
#[cfg(feature = "f16")]
arctan_f16,
f32::atan
);
binary_f16_fn!(
#[cfg(feature = "f16")]
arctan2_f16,
f32::atan2
);
binary_f16_fn!(
#[cfg(feature = "f16")]
hypot_f16,
f32::hypot
);
unary_f16_fn!(
#[cfg(feature = "f16")]
sinh_f16,
f32::sinh
);
unary_f16_fn!(
#[cfg(feature = "f16")]
cosh_f16,
f32::cosh
);
unary_f16_fn!(
#[cfg(feature = "f16")]
tanh_f16,
f32::tanh
);
unary_f16_fn!(
#[cfg(feature = "f16")]
arcsinh_f16,
f32::asinh
);
unary_f16_fn!(
#[cfg(feature = "f16")]
arccosh_f16,
f32::acosh
);
unary_f16_fn!(
#[cfg(feature = "f16")]
arctanh_f16,
f32::atanh
);
unary_f16_fn!(
#[cfg(feature = "f16")]
degrees_f16,
f32::to_degrees
);
unary_f16_fn!(
#[cfg(feature = "f16")]
radians_f16,
f32::to_radians
);
#[cfg(test)]
mod tests {
use super::*;
use crate::test_util::arr1;
#[test]
fn test_sin() {
let a = arr1(vec![0.0, std::f64::consts::FRAC_PI_2, std::f64::consts::PI]);
let r = sin(&a).unwrap();
let s = r.as_slice().unwrap();
assert!((s[0]).abs() < 1e-12);
assert!((s[1] - 1.0).abs() < 1e-12);
assert!((s[2]).abs() < 1e-12);
}
#[test]
fn test_cos() {
let a = arr1(vec![0.0, std::f64::consts::PI]);
let r = cos(&a).unwrap();
let s = r.as_slice().unwrap();
assert!((s[0] - 1.0).abs() < 1e-12);
assert!((s[1] + 1.0).abs() < 1e-12);
}
#[test]
fn test_tan() {
let a = arr1(vec![0.0, std::f64::consts::FRAC_PI_4]);
let r = tan(&a).unwrap();
let s = r.as_slice().unwrap();
assert!((s[0]).abs() < 1e-12);
assert!((s[1] - 1.0).abs() < 1e-12);
}
#[test]
fn test_arcsin() {
let a = arr1(vec![0.0, 1.0]);
let r = arcsin(&a).unwrap();
let s = r.as_slice().unwrap();
assert!((s[0]).abs() < 1e-12);
assert!((s[1] - std::f64::consts::FRAC_PI_2).abs() < 1e-12);
}
#[test]
fn test_arctan2() {
let y = arr1(vec![0.0, 1.0]);
let x = arr1(vec![1.0, 0.0]);
let r = arctan2(&y, &x).unwrap();
let s = r.as_slice().unwrap();
assert!((s[0]).abs() < 1e-12);
assert!((s[1] - std::f64::consts::FRAC_PI_2).abs() < 1e-12);
}
#[test]
fn test_hypot() {
let a = arr1(vec![3.0, 5.0]);
let b = arr1(vec![4.0, 12.0]);
let r = hypot(&a, &b).unwrap();
let s = r.as_slice().unwrap();
assert!((s[0] - 5.0).abs() < 1e-12);
assert!((s[1] - 13.0).abs() < 1e-12);
}
#[test]
fn test_sinh_cosh_tanh() {
let a = arr1(vec![0.0]);
assert!((sinh(&a).unwrap().as_slice().unwrap()[0]).abs() < 1e-12);
assert!((cosh(&a).unwrap().as_slice().unwrap()[0] - 1.0).abs() < 1e-12);
assert!((tanh(&a).unwrap().as_slice().unwrap()[0]).abs() < 1e-12);
}
#[test]
fn test_degrees_radians() {
let a = arr1(vec![std::f64::consts::PI]);
let deg = degrees(&a).unwrap();
assert!((deg.as_slice().unwrap()[0] - 180.0).abs() < 1e-10);
let back = radians(°).unwrap();
assert!((back.as_slice().unwrap()[0] - std::f64::consts::PI).abs() < 1e-12);
}
#[test]
fn test_deg2rad_rad2deg() {
let a = arr1(vec![180.0]);
let r = deg2rad(&a).unwrap();
assert!((r.as_slice().unwrap()[0] - std::f64::consts::PI).abs() < 1e-12);
let d = rad2deg(&arr1(vec![std::f64::consts::PI])).unwrap();
assert!((d.as_slice().unwrap()[0] - 180.0).abs() < 1e-10);
}
#[test]
fn test_unwrap_basic() {
let a = arr1(vec![0.0, 0.5, 1.0, -0.5, -1.0]);
let r = unwrap(&a, None).unwrap();
let s = r.as_slice().unwrap();
for (i, &v) in s.iter().enumerate() {
assert!((v - a.as_slice().unwrap()[i]).abs() < 1e-12);
}
}
#[test]
fn test_arcsinh_arccosh_arctanh() {
let a = arr1(vec![0.0]);
assert!((arcsinh(&a).unwrap().as_slice().unwrap()[0]).abs() < 1e-12);
let b = arr1(vec![1.0]);
assert!((arccosh(&b).unwrap().as_slice().unwrap()[0]).abs() < 1e-12);
assert!((arctanh(&a).unwrap().as_slice().unwrap()[0]).abs() < 1e-12);
}
#[cfg(feature = "f16")]
mod f16_tests {
use super::*;
use ferray_core::dimension::Ix1;
fn arr1_f16(data: &[f32]) -> Array<half::f16, Ix1> {
let n = data.len();
let vals: Vec<half::f16> = data.iter().map(|&x| half::f16::from_f32(x)).collect();
Array::from_vec(Ix1::new([n]), vals).unwrap()
}
#[test]
fn test_sin_f16() {
let a = arr1_f16(&[0.0, std::f32::consts::FRAC_PI_2, std::f32::consts::PI]);
let r = sin_f16(&a).unwrap();
let s = r.as_slice().unwrap();
assert!(s[0].to_f32().abs() < 0.01);
assert!((s[1].to_f32() - 1.0).abs() < 0.01);
assert!(s[2].to_f32().abs() < 0.01);
}
#[test]
fn test_cos_f16() {
let a = arr1_f16(&[0.0, std::f32::consts::PI]);
let r = cos_f16(&a).unwrap();
let s = r.as_slice().unwrap();
assert!((s[0].to_f32() - 1.0).abs() < 0.01);
assert!((s[1].to_f32() + 1.0).abs() < 0.01);
}
#[test]
fn test_tan_f16() {
let a = arr1_f16(&[0.0, std::f32::consts::FRAC_PI_4]);
let r = tan_f16(&a).unwrap();
let s = r.as_slice().unwrap();
assert!(s[0].to_f32().abs() < 0.01);
assert!((s[1].to_f32() - 1.0).abs() < 0.01);
}
#[test]
fn test_arctan2_f16() {
let y = arr1_f16(&[0.0, 1.0]);
let x = arr1_f16(&[1.0, 0.0]);
let r = arctan2_f16(&y, &x).unwrap();
let s = r.as_slice().unwrap();
assert!(s[0].to_f32().abs() < 0.01);
assert!((s[1].to_f32() - std::f32::consts::FRAC_PI_2).abs() < 0.01);
}
#[test]
fn test_degrees_radians_f16() {
let a = arr1_f16(&[std::f32::consts::PI]);
let deg = degrees_f16(&a).unwrap();
assert!((deg.as_slice().unwrap()[0].to_f32() - 180.0).abs() < 0.5);
let back = radians_f16(°).unwrap();
assert!((back.as_slice().unwrap()[0].to_f32() - std::f32::consts::PI).abs() < 0.01);
}
}
}