// Method :
// Based on
// acosh(x) = log [ x + sqrt(x*x-1) ]
// we have
// acosh(x) := log(x)+ln2, if x is large; else
// acosh(x) := log(2x-1/(sqrt(x*x-1)+x)) if x>2; else
// acosh(x) := log1p(t+sqrt(2.0*t+t*t)); where t=x-1.
//
// Special cases:
// acosh(x) is NaN with signal if x<1.
// acosh(NaN) is NaN without signal.
//
///|
/// Return the inverse hyperbolic cosine of x.
///
/// Returns the inverse hyperbolic cosine of `x`, defined as the value `y` such that `x = cosh(y)`.
///
/// # Examples
///
/// ```moonbit nocheck
/// assert_eq(acosh(1.0), 0.0);
/// assert_eq(acosh(2.0), 1.3169578969248166);
/// assert_eq(acosh(3.0), 1.7627471740390859);
/// assert_eq(acosh(4.0), 2.0634370688955608);
/// ```
///
/// # Special cases
///
/// 1. acosh(x) = NaN for all x < 1.
/// 2. aoosh(NaN) = NaN.
///
/// # Accuracy
///
/// 1 ulp
pub fn acosh(x : Double) -> Double {
let one = 1.0
let ln2 = 6.93147180559945286227e-01 // 0x3FE62E42, 0xFEFA39EF
let hx = __hi(x).reinterpret_as_int()
if x < 1.0 || isnan(x) {
// x < 1
return @double.not_a_number
} else if x == 1.0 {
return 0.0
} else if ispinf(x) {
return @double.infinity
} else if hx >= 0x41b00000 {
// x > 2**28
return log(x) + ln2
} else if hx > 0x40000000 {
// 2**28 > x > 2
let t = x * x
return log(2.0 * x - one / (x + sqrt(t - one)))
} else {
// 1 < x < 2
let t = x - one
return log1p(t + sqrt(2.0 * t + t * t))
}
}
///|
test "acosh" {
fn assert_acosh_ulp(input, expect) raise {
assert_ulp(expect, acosh(input), ACOSH_MAX_ULP)
}
assert_acosh_ulp(-1, @double.not_a_number)
assert_acosh_ulp(-2, @double.not_a_number)
assert_acosh_ulp(-3, @double.not_a_number)
assert_acosh_ulp(-4, @double.not_a_number)
assert_acosh_ulp(-5, @double.not_a_number)
assert_acosh_ulp(-6, @double.not_a_number)
assert_acosh_ulp(-7, @double.not_a_number)
assert_acosh_ulp(-8, @double.not_a_number)
assert_acosh_ulp(-9, @double.not_a_number)
assert_acosh_ulp(1, 0)
assert_acosh_ulp(2, 1.3169578969248166)
assert_acosh_ulp(3, 1.7627471740390859)
assert_acosh_ulp(4, 2.0634370688955608)
assert_acosh_ulp(5, 2.2924316695611777)
assert_acosh_ulp(6, 2.477888730288475)
assert_acosh_ulp(7, 2.6339157938496336)
assert_acosh_ulp(8, 2.7686593833135738)
assert_acosh_ulp(9, 2.8872709503576206)
assert_acosh_ulp(10, 2.993222846126381)
assert_acosh_ulp(100, 5.298292365610484)
assert_acosh_ulp(1000, 7.600902209541989)
assert_acosh_ulp(10000, 9.903487550036129)
assert_acosh_ulp(2.5, 1.566799236972411)
assert_acosh_ulp(3.4, 1.8945590126722978)
assert_acosh_ulp(5.3, 2.351832816454804)
assert_acosh_ulp(6.2, 2.5111284516219943)
assert_acosh_ulp(7.1, 2.6482453267067454)
assert_acosh_ulp(8.9, 2.8760272423851934)
assert_acosh_ulp(9.8, 2.9729162609330437)
assert_acosh_ulp(10.7, 3.0612001381589975)
assert_acosh_ulp(101.6, 5.314166496026017)
assert_acosh_ulp(1.542, 0.9990799125933573)
assert_acosh_ulp(2.846, 1.7066606669574687)
assert_acosh_ulp(7.881, 2.7535523517164964)
assert_acosh_ulp(3.772, 2.002699466791843)
assert_acosh_ulp(-1.542, @double.not_a_number)
assert_acosh_ulp(-2.846, @double.not_a_number)
assert_acosh_ulp(-7.881, @double.not_a_number)
assert_acosh_ulp(-3.772, @double.not_a_number)
assert_acosh_ulp(-1, @double.not_a_number)
assert_acosh_ulp(0, @double.not_a_number)
assert_acosh_ulp(-0, @double.not_a_number)
assert_acosh_ulp(@double.not_a_number, @double.not_a_number)
assert_acosh_ulp(@double.infinity, @double.infinity)
assert_acosh_ulp(@double.neg_infinity, @double.not_a_number)
}