///|
/// Compute the sine and cosine of a number simultaneously
///
/// # Examples
///
/// ```moonbit nocheck
/// let x = 1.0
/// let (sin_x, cos_x) = sincos(x)
/// let sin_x_directly = sin(x)
/// let cos_x_directly = cos(x)
/// assert_eq(sin_x, sin_x_directly)
/// assert_eq(cos_x, cos_x_directly)
/// ```
///
/// # Special Cases
///
/// 1. sincos(+-inf) = (NaN, NaN)
/// 2. sincos(NaN) = (NaN, NaN)
///
/// # Accuracy
///
/// Same as `sin` and `cos`, both are 1 ulp.
pub fn sincos(x : Double) -> (Double, Double) {
if isinf(x) || isnan(x) {
return (@double.not_a_number, @double.not_a_number)
}
let y = [0.0, 0.0]
let z = 0.0
let ix = __hi(x).reinterpret_as_int() & 0x7fffffff
if ix <= 0x3fe921fb {
return (__kernel_sin(x, z, 0), __kernel_cos(x, z))
} else {
let n = rem_pio2(x, y)
match n & 3 {
0 => (__kernel_sin(y[0], y[1], 1), __kernel_cos(y[0], y[1]))
1 => (__kernel_cos(y[0], y[1]), -__kernel_sin(y[0], y[1], 1))
2 => (-__kernel_sin(y[0], y[1], 1), -__kernel_cos(y[0], y[1]))
_ => (-__kernel_cos(y[0], y[1]), __kernel_sin(y[0], y[1], 1))
}
}
}
///|
test "sincos" {
let arr_x = [
0.0, 1.0, 2.0, 3.0, 4.0, 0.7853981633974483, 1.5707963267948966, 3.141592653589793,
10000.0,
]
for x in arr_x {
let sin_x = sin(x)
let cos_x = cos(x)
let (_sin_x, _cos_x) = sincos(x)
assert_eq(_sin_x, sin_x)
assert_eq(_cos_x, cos_x)
}
}