///|
/// Compute the principal value of the arc cosine of `x`.
///
/// # Examples
///
/// ```moonbit nocheck
/// assert_eq(acos(-0.5), 2.0943951023931957)
/// assert_eq(acos(0.5), 1.0471975511965979)
/// assert_eq(acos(1), 0)
/// assert_eq(acos(0), 1.5707963267948966)
/// assert_eq(acos(-1), 3.141592653589793)
/// ```
///
/// # Accruacy
///
/// 1 ulp (unit in the last place).
///
/// # Special cases
///
/// 1. If `x` is NaN, a NaN is returned.
/// 2. If |x| > 1, a NaN is returned with the invalid signal raised.
pub fn acos(x : Double) -> Double {
let one : Double = 1.0
let pi : Double = 3.14159265358979311600
let pio2_hi : Double = 1.57079632679489655800
let pio2_lo : Double = 6.12323399573676603587e-17
let ps0 : Double = 1.66666666666666657415e-01
let ps1 : Double = -3.25565818622400915405e-01
let ps2 : Double = 2.01212532134862925881e-01
let ps3 : Double = -4.00555345006794114027e-02
let ps4 : Double = 7.91534994289814532176e-04
let ps5 : Double = 3.47933107596021167570e-05
let qs1 : Double = -2.40339491173441421878e+00
let qs2 : Double = 2.02094576023350569471e+00
let qs3 : Double = -6.88283971605453293030e-01
let qs4 : Double = 7.70381505559019352791e-02
let ix = __hi(x).reinterpret_as_int() & 0x7fffffff
let absx = x.abs()
if absx >= 1.0 {
if absx == 1.0 {
if x > 0 {
return 0.0
} else {
return pi + 2.0 * pio2_lo
}
}
return @double.not_a_number
}
if absx < 0.5 {
if ix <= 0x3c600000 {
return pio2_hi + pio2_lo
}
let z = x * x
let p = z * (ps0 + z * (ps1 + z * (ps2 + z * (ps3 + z * (ps4 + z * ps5)))))
let q = one + z * (qs1 + z * (qs2 + z * (qs3 + z * qs4)))
let r = p / q
pio2_hi - (x - (pio2_lo - x * r))
} else if x < 0 {
let z = (one + x) * 0.5
let p = z * (ps0 + z * (ps1 + z * (ps2 + z * (ps3 + z * (ps4 + z * ps5)))))
let q = one + z * (qs1 + z * (qs2 + z * (qs3 + z * qs4)))
let s = z.sqrt()
let r = p / q
let w = r * s - pio2_lo
pi - 2.0 * (s + w)
} else {
let z = (one - x) * 0.5
let s = z.sqrt()
let df = s
let c = (z - df * df) / (s + df)
let p = z * (ps0 + z * (ps1 + z * (ps2 + z * (ps3 + z * (ps4 + z * ps5)))))
let q = one + z * (qs1 + z * (qs2 + z * (qs3 + z * qs4)))
let r = p / q
let w = r * s + c
2.0 * (df + w)
}
}
///|
test "acos" {
fn assert_acos_ulp(input, expect) raise {
assert_ulp(expect, acos(input), ACOS_MAX_ULP)
}
assert_acos_ulp(0, 1.5707963267948966)
assert_acos_ulp(1, 0)
assert_acos_ulp(2, @double.not_a_number)
assert_acos_ulp(-1, 3.141592653589793)
assert_acos_ulp(-2, @double.not_a_number)
assert_acos_ulp(0.12, 1.4505064444001086)
assert_acos_ulp(0.23, 1.3387186439321834)
assert_acos_ulp(0.34, 1.2238794292677349)
assert_acos_ulp(0.45, 1.1040309877476002)
assert_acos_ulp(0.56, 0.9764105267938343)
assert_acos_ulp(0.67, 0.8365875393415376)
assert_acos_ulp(0.78, 0.6761305095606613)
assert_acos_ulp(0.89, 0.47345115727206616)
assert_acos_ulp(0.9, 0.45102681179626236)
assert_acos_ulp(-0.87, 2.6259986473437027)
assert_acos_ulp(-0.76, 2.4341094418104503)
assert_acos_ulp(-0.65, 2.278380763520252)
assert_acos_ulp(-0.54, 2.1412334361948187)
assert_acos_ulp(-0.43, 2.0152891037307157)
assert_acos_ulp(-0.32, 1.8965258140895267)
assert_acos_ulp(-0.21, 1.7823712865529922)
assert_acos_ulp(-0.1, 1.6709637479564563)
assert_acos_ulp(0.12345, 1.447030595701842)
assert_acos_ulp(0.23456, 1.3340304110004462)
assert_acos_ulp(0.34567, 1.2178436209004186)
assert_acos_ulp(0.45678, 1.0964241932520653)
assert_acos_ulp(0.56789, 0.966856210975062)
assert_acos_ulp(0.6789, 0.8245328984417092)
assert_acos_ulp(0.78901, 0.661600380485764)
assert_acos_ulp(0.89012, 0.4731879089572146)
assert_acos_ulp(0.90123, 0.4481967258405143)
assert_acos_ulp(-0.98765, 2.9842681197855034)
assert_acos_ulp(-0.87654, 2.639422356361353)
assert_acos_ulp(-0.76543, 2.442505592710677)
assert_acos_ulp(-0.65432, 2.2840793852039942)
assert_acos_ulp(-0.54321, 2.145051991929172)
assert_acos_ulp(-0.4321, 2.017616417853647)
assert_acos_ulp(-0.32109, 1.8976765338688588)
assert_acos_ulp(-0.21098, 1.7833737456834884)
assert_acos_ulp(-0.10987, 1.6808885840186583)
assert_acos_ulp(0.5772157, 0.9554814214475389)
assert_acos_ulp(0.3010299, 1.2650238606722306)
assert_acos_ulp(0.6931472, 0.8049501050055602)
assert_acos_ulp(0.4342945, 1.1215414160225383)
assert_acos_ulp(0.0000001, 1.5707962267948965)
assert_acos_ulp(@double.max_value, @double.not_a_number)
assert_acos_ulp(@double.min_value, @double.not_a_number)
assert_acos_ulp(@double.infinity, @double.not_a_number)
assert_acos_ulp(@double.neg_infinity, @double.not_a_number)
assert_acos_ulp(@double.not_a_number, @double.not_a_number)
}