use crate::InnerFloat::{Finite, Infinity, NaN, Zero};
use crate::float::arithmetic::atan::alternating_odd_series;
use crate::float::arithmetic::cosh::{monotone_rational_via_floats, same_rounding};
use crate::float::arithmetic::round_near_x::{
LEADING_TERM_MIN_EXPONENT, round_rational_leading_term, small_input_shortcut,
};
use crate::float::arithmetic::sin::{UNDERFLOW_EXPONENT, underflowed};
use crate::{Float, emulate_float_to_float_fn, emulate_rational_to_float_fn};
use core::cmp::Ordering::{self, Equal};
use core::cmp::max;
use malachite_base::fail_on_untested_path;
use malachite_base::num::arithmetic::traits::{Abs, Asinh, AsinhAssign, CeilingLogBase2, Ln, Sqrt};
use malachite_base::num::basic::floats::PrimitiveFloat;
use malachite_base::num::basic::integers::PrimitiveInt;
use malachite_base::num::basic::traits::{NaN as NaNTrait, One, Zero as ZeroTrait};
use malachite_base::num::conversion::traits::{ExactFrom, RoundingFrom};
use malachite_base::num::logic::traits::SignificantBits;
use malachite_base::rounding_modes::RoundingMode::{self, *};
use malachite_nz::natural::arithmetic::float::round::float_can_round;
use malachite_nz::platform::Limb;
use malachite_q::Rational;
const LN_2_SHORTCUT_MAX_PREC: u64 = (1 << 30) + 28;
pub(crate) fn asinh_abs_general(x_abs: &Float, wp: u64) -> Float {
x_abs
.square_prec_round_ref(wp, Floor)
.0
.add_round(Float::ONE, Floor)
.0
.sqrt()
.add_prec_val_ref(x_abs, wp)
.0
.ln()
}
pub(crate) fn ln_of_large_sum(x: &Float, wp: u64, plus: bool) -> Float {
let ln_x = x.ln_prec_ref(wp).0;
let correction = if wp <= LN_2_SHORTCUT_MAX_PREC {
let exp_ln_x = u64::from(ln_x.get_exponent().unwrap().unsigned_abs());
Float::ln_2_prec(wp.saturating_sub(exp_ln_x).max(1)).0
} else {
fail_on_untested_path("ln_of_large_sum, full correction");
let reciprocal_squared = x
.reciprocal_prec_round_ref(wp, Floor)
.0
.square_round(Floor)
.0;
let inner = if plus {
Float::ONE.add_round(reciprocal_squared, Floor).0
} else {
Float::ONE.sub_round(reciprocal_squared, Floor).0
};
(inner.sqrt() + Float::ONE).ln()
};
ln_x + correction
}
pub(crate) const fn square_may_overflow(x: &Float) -> bool {
x.get_exponent().unwrap() > Float::MAX_EXPONENT >> 1
}
pub(crate) fn round_with_error(
t: Float,
wp: u64,
err: i64,
prec: u64,
rm: RoundingMode,
) -> Option<(Float, Ordering)> {
let bits = i64::exact_from(wp) - err;
(bits > 0
&& float_can_round(
t.significand_ref().unwrap(),
u64::exact_from(bits),
prec,
rm,
))
.then(|| Float::from_float_prec_round(t, prec, rm))
}
fn asinh_prec_round_normal_ref(x: &Float, prec: u64, rm: RoundingMode) -> (Float, Ordering) {
assert_ne!(rm, Exact, "Inexact asinh");
let exp_x = i64::from(x.get_exponent().unwrap());
if let Some(result) = small_input_shortcut(x, -(exp_x << 1), 2, false, prec, rm) {
return result;
}
let negative = *x < 0u32;
let x_abs = x.abs();
let large = square_may_overflow(x);
let mut working_prec = prec + 4 + prec.ceiling_log_base_2();
let mut increment = Limb::WIDTH;
loop {
let t = if large {
ln_of_large_sum(&x_abs, working_prec, true)
} else {
asinh_abs_general(&x_abs, working_prec)
};
if t.is_normal() {
let err = if large {
2
} else {
max(4 - i64::from(t.get_exponent().unwrap()), 0) + 1
};
if let Some(result) =
round_with_error(if negative { -t } else { t }, working_prec, err, prec, rm)
{
return result;
}
}
working_prec += increment;
increment = working_prec >> 1;
}
}
fn asinh_series(x: &Rational, prec: u64, rm: RoundingMode) -> (Float, Ordering) {
alternating_odd_series(x, prec, rm, |k| {
Some(Rational::from_unsigneds((k << 1) - 1, k << 1))
})
}
pub(crate) fn ln_of_large_rational_sum(
x: &Rational,
exp_x: i64,
prec: u64,
rm: RoundingMode,
plus: bool,
) -> (Float, Ordering) {
let two_x = x << 1u32;
let mut working_prec = prec + 10;
let mut increment = Limb::WIDTH;
loop {
assert!(
working_prec < u64::exact_from(exp_x) << 1,
"ln_of_large_rational_sum needs a working precision below 2 EXP(x)"
);
let mut lo = Float::ln_rational_prec_round_ref(&two_x, working_prec, Floor).0;
let mut hi = Float::ln_rational_prec_round_ref(&two_x, working_prec, Ceiling).0;
if plus {
hi.increment();
} else {
lo.decrement();
}
if let Some(result) = same_rounding(
Float::from_float_prec_round(lo, prec, rm),
Float::from_float_prec_round(hi, prec, rm),
) {
return result;
}
fail_on_untested_path("ln_of_large_rational_sum, retry");
working_prec += increment;
increment = working_prec >> 1;
}
}
fn asinh_rational_huge(x: &Rational, exp_x: i64, prec: u64, rm: RoundingMode) -> (Float, Ordering) {
if *x > 0u32 {
ln_of_large_rational_sum(x, exp_x, prec, rm, true)
} else {
let (y, o) = ln_of_large_rational_sum(&-x, exp_x, prec, -rm, true);
(-y, o.reverse())
}
}
fn asinh_rational_helper(x: &Rational, prec: u64, rm: RoundingMode) -> (Float, Ordering) {
assert_ne!(rm, Exact, "Inexact asinh");
let exp_x = x.floor_log_base_2_abs() + 1; if exp_x < UNDERFLOW_EXPONENT {
return underflowed(*x > 0u32, prec, rm);
}
if exp_x > LEADING_TERM_MIN_EXPONENT
&& -(exp_x << 1) > i64::exact_from(prec + x.denominator_ref().significant_bits()) + 4
{
return round_rational_leading_term(x.abs(), *x > 0u32, false, prec, rm);
}
if exp_x < 0 && -(exp_x << 2) > i64::exact_from(prec) + 3 {
return asinh_series(x, prec, rm);
}
if exp_x > Float::MAX_EXPONENT_I64 {
return asinh_rational_huge(x, exp_x, prec, rm);
}
monotone_rational_via_floats(x, prec, rm, asinh_prec_round_normal_ref)
}
impl Float {
#[inline]
pub fn asinh_prec_round(self, prec: u64, rm: RoundingMode) -> (Self, Ordering) {
self.asinh_prec_round_ref(prec, rm)
}
pub fn asinh_prec_round_ref(&self, prec: u64, rm: RoundingMode) -> (Self, Ordering) {
assert_ne!(prec, 0);
match &self.0 {
NaN => (Self::NAN, Equal),
Infinity { .. } | Zero { .. } => (self.clone(), Equal),
Finite { .. } => asinh_prec_round_normal_ref(self, prec, rm),
}
}
#[inline]
pub fn asinh_prec(self, prec: u64) -> (Self, Ordering) {
self.asinh_prec_round(prec, Nearest)
}
#[inline]
pub fn asinh_prec_ref(&self, prec: u64) -> (Self, Ordering) {
self.asinh_prec_round_ref(prec, Nearest)
}
#[inline]
pub fn asinh_round(self, rm: RoundingMode) -> (Self, Ordering) {
let prec = self.significant_bits();
self.asinh_prec_round(prec, rm)
}
#[inline]
pub fn asinh_round_ref(&self, rm: RoundingMode) -> (Self, Ordering) {
self.asinh_prec_round_ref(self.significant_bits(), rm)
}
#[inline]
pub fn asinh_prec_round_assign(&mut self, prec: u64, rm: RoundingMode) -> Ordering {
let o;
(*self, o) = self.asinh_prec_round_ref(prec, rm);
o
}
#[inline]
pub fn asinh_prec_assign(&mut self, prec: u64) -> Ordering {
self.asinh_prec_round_assign(prec, Nearest)
}
#[inline]
pub fn asinh_round_assign(&mut self, rm: RoundingMode) -> Ordering {
let prec = self.significant_bits();
self.asinh_prec_round_assign(prec, rm)
}
}
impl Float {
#[inline]
#[allow(clippy::needless_pass_by_value)]
pub fn asinh_rational_prec_round(x: Rational, prec: u64, rm: RoundingMode) -> (Self, Ordering) {
Self::asinh_rational_prec_round_ref(&x, prec, rm)
}
pub fn asinh_rational_prec_round_ref(
x: &Rational,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
assert_ne!(prec, 0);
if *x == 0u32 {
return (Self::ZERO, Equal);
}
asinh_rational_helper(x, prec, rm)
}
#[inline]
#[allow(clippy::needless_pass_by_value)]
pub fn asinh_rational_prec(x: Rational, prec: u64) -> (Self, Ordering) {
Self::asinh_rational_prec_round_ref(&x, prec, Nearest)
}
#[inline]
pub fn asinh_rational_prec_ref(x: &Rational, prec: u64) -> (Self, Ordering) {
Self::asinh_rational_prec_round_ref(x, prec, Nearest)
}
}
impl Asinh for Float {
type Output = Self;
#[inline]
fn asinh(self) -> Self {
let prec = self.significant_bits();
self.asinh_prec_round(prec, Nearest).0
}
}
impl Asinh for &Float {
type Output = Float;
#[inline]
fn asinh(self) -> Float {
self.asinh_prec_round_ref(self.significant_bits(), Nearest)
.0
}
}
impl AsinhAssign for Float {
#[inline]
fn asinh_assign(&mut self) {
let prec = self.significant_bits();
self.asinh_prec_round_assign(prec, Nearest);
}
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_asinh<T: PrimitiveFloat>(x: T) -> T
where
Float: From<T> + PartialOrd<T>,
for<'a> T: ExactFrom<&'a Float> + RoundingFrom<&'a Float>,
{
emulate_float_to_float_fn(Float::asinh_prec, x)
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_asinh_rational<T: PrimitiveFloat>(x: &Rational) -> T
where
Float: PartialOrd<T>,
for<'a> T: ExactFrom<&'a Float> + RoundingFrom<&'a Float>,
{
emulate_rational_to_float_fn(Float::asinh_rational_prec_ref, x)
}