use crate::InnerFloat::{Finite, Infinity, NaN, Zero};
use crate::float::arithmetic::acsch::RECIPROCAL_SAFE_EXPONENT;
use crate::float::arithmetic::asinh::round_with_error;
use crate::float::arithmetic::cosh::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::{TINY_UNDERFLOW_EXPONENT, underflowed};
use crate::{Float, emulate_float_to_float_fn, emulate_rational_to_float_fn};
use core::cmp::Ordering::{self, *};
use core::cmp::max;
use malachite_base::fail_on_untested_path;
use malachite_base::num::arithmetic::traits::{
Abs, Atanh, AtanhAssign, CeilingLogBase2, IsPowerOf2, Ln, Ln1PlusX, Square,
};
use malachite_base::num::basic::floats::PrimitiveFloat;
use malachite_base::num::basic::integers::PrimitiveInt;
use malachite_base::num::basic::traits::{
Infinity as InfinityTrait, NaN as NaNTrait, NegativeInfinity, NegativeZero, One, Two,
Zero as ZeroTrait,
};
use malachite_base::num::comparison::traits::OrdAbs;
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::platform::Limb;
use malachite_q::Rational;
fn atanh_small(x: &Float, p: u64) -> (Float, u64) {
let mut t = Float::from_float_prec_ref(x, p).0;
let mut y = t.clone();
let x2 = x.square_prec_ref(p).0;
let p_i64 = i64::exact_from(p);
let mut i = 3u64;
let mut i_float = Float::from_unsigned_prec(3u32, 64).0;
loop {
t *= &x2;
let u = t.div_prec_ref_ref(&i_float, p).0;
if u == 0u32 {
fail_on_untested_path("atanh_small, u underflows");
break;
}
if i64::from(u.get_exponent().unwrap()) <= i64::from(y.get_exponent().unwrap()) - p_i64 {
break;
}
y += u;
i += 2;
i_float += Float::TWO;
}
let err = (i + 8) >> 1; let k = err.ceiling_log_base_2();
assert!(k + 2 < p);
(y, k)
}
fn atanh_prec_round_normal_ref(xt: &Float, prec: u64, rm: RoundingMode) -> (Float, Ordering) {
assert_ne!(rm, Exact, "Inexact atanh");
let exp_xt = i64::from(xt.get_exponent().unwrap());
if let Some(result) = small_input_shortcut(xt, -(exp_xt << 1), 1, true, prec, rm) {
return result;
}
let negative = *xt < 0u32;
let x = xt.abs();
let nt = max(x.get_prec().unwrap(), prec);
let mut working_prec = nt + nt.ceiling_log_base_2() + 4;
let mut increment = Limb::WIDTH;
let k = 1 + prec.ceiling_log_base_2();
let small = exp_xt <= -1 - i64::exact_from(prec / k);
loop {
let (t, err) = if small {
let (t, k) = atanh_small(&x, working_prec);
(t, i64::exact_from(k))
} else {
let te = Float::ONE
.sub_prec_round_val_ref(&x, working_prec, Ceiling)
.0;
if i64::from(te.get_exponent().unwrap()) < RECIPROCAL_SAFE_EXPONENT {
fail_on_untested_path("atanh_prec_round_normal_ref, (1+x)/(1-x) may overflow");
let t = (x
.add_prec_round_ref_val(Float::ONE, working_prec, Floor)
.0
.ln()
- te.ln())
>> 1u32;
let err = max(4 - i64::from(t.get_exponent().unwrap()), 0) + 1;
(t, err)
} else {
let t = (&x << 1u32).div_prec(te, working_prec).0.ln_1_plus_x() >> 1u32;
(t, 3)
}
};
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 atanh_series(x: &Rational, prec: u64, rm: RoundingMode) -> (Float, Ordering) {
let x2 = x.square();
let one_minus_x2 = Rational::ONE - &x2;
let mut power = x.clone(); let mut sum = x.clone();
let mut k = 1u64;
loop {
power *= &x2;
k += 2;
let bound = &power / (Rational::from(k) * &one_minus_x2);
if let Some(result) = same_rounding(
Float::from_rational_prec_round_ref(&sum, prec, rm),
Float::from_rational_prec_round(&sum + bound, prec, rm),
) {
return result;
}
sum += &power / Rational::from(k);
}
}
fn atanh_rational_helper(x: &Rational, prec: u64, rm: RoundingMode) -> (Float, Ordering) {
assert_ne!(rm, Exact, "Inexact atanh");
let positive = *x > 0u32;
let exp_x = x.floor_log_base_2_abs() + 1; if exp_x < TINY_UNDERFLOW_EXPONENT {
return underflowed(positive, 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(), positive, true, prec, rm);
}
if exp_x < 0
&& (u128::from(exp_x.unsigned_abs()).pow(2)
* 12
* u128::from(1 + prec.ceiling_log_base_2())
> u128::from(prec)
.saturating_mul(u128::from(x.denominator_ref().significant_bits()) + 64)
|| exp_x <= LEADING_TERM_MIN_EXPONENT)
{
return atanh_series(x, prec, rm);
}
atanh_rational_via_ln(x, prec, rm)
}
fn atanh_rational_via_ln(x: &Rational, prec: u64, rm: RoundingMode) -> (Float, Ordering) {
let n = x.numerator_ref();
let d = x.denominator_ref();
let (sum, difference) = (d + n, d - n);
let q = if *x > 0u32 {
Rational::from_naturals(sum, difference)
} else {
Rational::from_naturals(difference, sum)
};
let (y, o) = Float::ln_rational_prec_round(q, prec, rm);
(y >> 1u32, o)
}
impl Float {
#[inline]
pub fn atanh_prec_round(self, prec: u64, rm: RoundingMode) -> (Self, Ordering) {
self.atanh_prec_round_ref(prec, rm)
}
pub fn atanh_prec_round_ref(&self, prec: u64, rm: RoundingMode) -> (Self, Ordering) {
assert_ne!(prec, 0);
match &self.0 {
NaN | Infinity { .. } => (Self::NAN, Equal),
Zero { sign } => (
if *sign {
Self::ZERO
} else {
Self::NEGATIVE_ZERO
},
Equal,
),
Finite {
sign,
exponent,
significand,
..
} if *exponent > 0 => {
if *exponent == 1 && significand.is_power_of_2() {
(
if *sign {
Self::INFINITY
} else {
Self::NEGATIVE_INFINITY
},
Equal,
)
} else {
(Self::NAN, Equal)
}
}
Finite { .. } => atanh_prec_round_normal_ref(self, prec, rm),
}
}
#[inline]
pub fn atanh_prec(self, prec: u64) -> (Self, Ordering) {
self.atanh_prec_round(prec, Nearest)
}
#[inline]
pub fn atanh_prec_ref(&self, prec: u64) -> (Self, Ordering) {
self.atanh_prec_round_ref(prec, Nearest)
}
#[inline]
pub fn atanh_round(self, rm: RoundingMode) -> (Self, Ordering) {
let prec = self.significant_bits();
self.atanh_prec_round(prec, rm)
}
#[inline]
pub fn atanh_round_ref(&self, rm: RoundingMode) -> (Self, Ordering) {
self.atanh_prec_round_ref(self.significant_bits(), rm)
}
#[inline]
pub fn atanh_prec_round_assign(&mut self, prec: u64, rm: RoundingMode) -> Ordering {
let o;
(*self, o) = self.atanh_prec_round_ref(prec, rm);
o
}
#[inline]
pub fn atanh_prec_assign(&mut self, prec: u64) -> Ordering {
self.atanh_prec_round_assign(prec, Nearest)
}
#[inline]
pub fn atanh_round_assign(&mut self, rm: RoundingMode) -> Ordering {
let prec = self.significant_bits();
self.atanh_prec_round_assign(prec, rm)
}
#[inline]
#[allow(clippy::needless_pass_by_value)]
pub fn atanh_rational_prec_round(x: Rational, prec: u64, rm: RoundingMode) -> (Self, Ordering) {
Self::atanh_rational_prec_round_ref(&x, prec, rm)
}
pub fn atanh_rational_prec_round_ref(
x: &Rational,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
assert_ne!(prec, 0);
if *x == 0u32 {
return (Self::ZERO, Equal);
}
match x.cmp_abs(&Rational::ONE) {
Less => atanh_rational_helper(x, prec, rm),
Equal => (
if *x > 0u32 {
Self::INFINITY
} else {
Self::NEGATIVE_INFINITY
},
Equal,
),
Greater => (Self::NAN, Equal),
}
}
#[inline]
#[allow(clippy::needless_pass_by_value)]
pub fn atanh_rational_prec(x: Rational, prec: u64) -> (Self, Ordering) {
Self::atanh_rational_prec_round_ref(&x, prec, Nearest)
}
#[inline]
pub fn atanh_rational_prec_ref(x: &Rational, prec: u64) -> (Self, Ordering) {
Self::atanh_rational_prec_round_ref(x, prec, Nearest)
}
}
impl Atanh for Float {
type Output = Self;
#[inline]
fn atanh(self) -> Self {
let prec = self.significant_bits();
self.atanh_prec_round(prec, Nearest).0
}
}
impl Atanh for &Float {
type Output = Float;
#[inline]
fn atanh(self) -> Float {
self.atanh_prec_round_ref(self.significant_bits(), Nearest)
.0
}
}
impl AtanhAssign for Float {
#[inline]
fn atanh_assign(&mut self) {
let prec = self.significant_bits();
self.atanh_prec_round_assign(prec, Nearest);
}
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_atanh<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::atanh_prec, x)
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_atanh_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::atanh_rational_prec_ref, x)
}