use crate::InnerFloat::{Finite, Infinity, NaN, Zero};
use crate::float::arithmetic::cos::round_bracket;
use crate::float::arithmetic::cosh::monotone_rational_via_floats;
use crate::float::arithmetic::round_near_x::{float_round_near_x, small_input_shortcut};
use crate::float::arithmetic::sin::{UNDERFLOW_EXPONENT, underflowed};
use crate::float::arithmetic::sinh::sinh_bound;
use crate::{Float, emulate_float_to_float_fn, emulate_rational_to_float_fn};
use core::cmp::Ordering::{self, Equal};
use core::cmp::{max, min};
use malachite_base::num::arithmetic::traits::{
Abs, CeilingLogBase2, FloorLogBase2, PowerOf2, Square, Tanh, TanhAssign,
};
use malachite_base::num::basic::floats::PrimitiveFloat;
use malachite_base::num::basic::integers::PrimitiveInt;
use malachite_base::num::basic::traits::{
NaN as NaNTrait, NegativeOne, One, Two, 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::Natural;
use malachite_nz::natural::arithmetic::float::round::float_can_round;
use malachite_nz::platform::Limb;
use malachite_q::Rational;
pub(crate) fn two_x_log_2_e_lower_bound(x_abs: &Float) -> u64 {
let f = if x_abs.get_exponent().unwrap() > 62 {
u64::MAX
} else {
u64::rounding_from(&(x_abs << 1u32), Floor).0
};
f.saturating_add((f >> 4) * 7)
}
fn tanh_near_one(x_abs: &Float, positive: bool, prec: u64, rm: RoundingMode) -> (Float, Ordering) {
let err = two_x_log_2_e_lower_bound(x_abs);
let one = if positive {
Float::ONE
} else {
Float::NEGATIVE_ONE
};
if err > prec + 1
&& let Some(result) = float_round_near_x(&one, min(err, prec + 2), false, prec, rm)
{
return result;
}
tanh_via_exp_x_minus_1(x_abs, positive, prec, rm)
}
fn tanh_via_exp_x_minus_1(
x_abs: &Float,
positive: bool,
prec: u64,
rm: RoundingMode,
) -> (Float, Ordering) {
let mut working_prec = prec + prec.ceiling_log_base_2() + 10;
let mut increment = Limb::WIDTH;
loop {
let e = (-(x_abs << 1u32)).exp_x_minus_1_prec(working_prec).0;
let denominator = &e + Float::TWO;
let t = -e / denominator;
if float_can_round(t.significand_ref().unwrap(), working_prec - 3, prec, rm) {
return Float::from_float_prec_round(if positive { t } else { -t }, prec, rm);
}
working_prec += increment;
increment = working_prec >> 1;
}
}
fn tanh_prec_round_normal_ref(xt: &Float, prec: u64, rm: RoundingMode) -> (Float, Ordering) {
assert_ne!(rm, Exact, "Inexact tanh");
let exp_xt = i64::from(xt.get_exponent().unwrap());
if let Some(result) = small_input_shortcut(xt, -(exp_xt << 1), 1, false, prec, rm) {
return result;
}
let x = xt.abs();
let positive = xt.is_sign_positive();
if x >= const { Float::MAX_EXPONENT >> 1 } {
return tanh_near_one(&x, positive, prec, rm);
}
let mut working_prec = prec + prec.ceiling_log_base_2() + 4;
if exp_xt < 0 {
working_prec += u64::exact_from(-exp_xt);
}
let two_x = &x << 1u32;
let mut increment = Limb::WIDTH;
loop {
let mut exp_2x = two_x.exp_prec_ref(working_prec).0;
if exp_2x.is_infinite() {
return tanh_near_one(&x, positive, prec, rm);
}
let exp_exp_2x = i64::from(exp_2x.get_exponent().unwrap());
let denominator = exp_2x.add_round_ref_val(Float::ONE, Floor).0;
exp_2x.sub_round_assign(Float::ONE, Ceiling);
let k = exp_exp_2x - i64::from(exp_2x.get_exponent().unwrap());
let quotient = exp_2x / denominator;
let d = max(3, k + 1);
let err = i64::exact_from(working_prec) - (d + 1);
if d <= i64::exact_from(working_prec >> 1)
&& float_can_round(
quotient.significand_ref().unwrap(),
u64::exact_from(err),
prec,
rm,
)
{
return Float::from_float_prec_round(
if positive { quotient } else { -quotient },
prec,
rm,
);
}
if quotient.get_exponent() == Some(1) {
return tanh_near_one(&x, positive, prec, rm);
}
working_prec += increment;
increment = working_prec >> 1;
}
}
pub(crate) fn cosh_bound(t: &Rational, w: u64, upper: bool) -> Rational {
let log = t.floor_log_base_2_abs();
assert!(log < -1);
let mut k = 1u64;
let mut log_factorial = 1u64; let target = -i128::from(w) - 4;
while i128::from(k << 1) * i128::from(log + 1) - i128::from(log_factorial) > target {
k += 1;
let two_k = k << 1;
log_factorial += (two_k - 1).floor_log_base_2() + two_k.floor_log_base_2();
}
let mut c = Rational::ONE;
if k > 1 {
let t_squared = t.square();
let mut term = Rational::ONE;
for j in 1..k {
term *= &t_squared;
term /= Rational::from(((j << 1) - 1) * (j << 1));
c += &term;
}
}
if upper {
let shift = w + 3;
c *= Rational::from(Natural::power_of_2(shift) + Natural::ONE);
c >>= shift;
}
c
}
fn tanh_rational_series(x: &Rational, prec: u64, rm: RoundingMode) -> (Float, Ordering) {
let mut w = prec + 10;
let mut increment = Limb::WIDTH;
loop {
let toward_zero = sinh_bound(x, w, false) / cosh_bound(x, w, true);
let away_from_zero = sinh_bound(x, w, true) / cosh_bound(x, w, false);
let (lo, hi) = if *x > 0u32 {
(toward_zero, away_from_zero)
} else {
(away_from_zero, toward_zero)
};
if let Some(result) = round_bracket(&lo, &hi, prec, rm) {
return result;
}
w += increment;
increment = w >> 1;
}
}
fn tanh_rational_helper(x: &Rational, prec: u64, rm: RoundingMode) -> (Float, Ordering) {
assert_ne!(rm, Exact, "Inexact tanh");
let positive = *x > 0u32;
let exp_x = x.floor_log_base_2_abs() + 1; if exp_x < UNDERFLOW_EXPONENT {
return underflowed(positive, prec, rm);
}
if exp_x < -1 && u64::exact_from(-exp_x) << 4 >= prec + 10 {
return tanh_rational_series(x, prec, rm);
}
if exp_x >= Float::MAX_EXPONENT_I64 {
let one = if positive {
Float::ONE
} else {
Float::NEGATIVE_ONE
};
return float_round_near_x(&one, prec + 2, false, prec, rm).unwrap();
}
monotone_rational_via_floats(x, prec, rm, tanh_prec_round_normal_ref)
}
impl Float {
#[inline]
pub fn tanh_prec_round(self, prec: u64, rm: RoundingMode) -> (Self, Ordering) {
self.tanh_prec_round_ref(prec, rm)
}
pub fn tanh_prec_round_ref(&self, prec: u64, rm: RoundingMode) -> (Self, Ordering) {
assert_ne!(prec, 0);
match &self.0 {
NaN => (Self::NAN, Equal),
Infinity { sign } => (
if *sign {
Self::one_prec(prec)
} else {
-Self::one_prec(prec)
},
Equal,
),
Zero { .. } => (self.clone(), Equal),
Finite { .. } => tanh_prec_round_normal_ref(self, prec, rm),
}
}
#[inline]
pub fn tanh_prec(self, prec: u64) -> (Self, Ordering) {
self.tanh_prec_round(prec, Nearest)
}
#[inline]
pub fn tanh_prec_ref(&self, prec: u64) -> (Self, Ordering) {
self.tanh_prec_round_ref(prec, Nearest)
}
#[inline]
pub fn tanh_round(self, rm: RoundingMode) -> (Self, Ordering) {
let prec = self.significant_bits();
self.tanh_prec_round(prec, rm)
}
#[inline]
pub fn tanh_round_ref(&self, rm: RoundingMode) -> (Self, Ordering) {
self.tanh_prec_round_ref(self.significant_bits(), rm)
}
#[inline]
pub fn tanh_prec_round_assign(&mut self, prec: u64, rm: RoundingMode) -> Ordering {
let o;
(*self, o) = self.tanh_prec_round_ref(prec, rm);
o
}
#[inline]
pub fn tanh_prec_assign(&mut self, prec: u64) -> Ordering {
self.tanh_prec_round_assign(prec, Nearest)
}
#[inline]
pub fn tanh_round_assign(&mut self, rm: RoundingMode) -> Ordering {
let prec = self.significant_bits();
self.tanh_prec_round_assign(prec, rm)
}
}
impl Float {
#[allow(clippy::needless_pass_by_value)]
#[inline]
pub fn tanh_rational_prec_round(x: Rational, prec: u64, rm: RoundingMode) -> (Self, Ordering) {
Self::tanh_rational_prec_round_ref(&x, prec, rm)
}
pub fn tanh_rational_prec_round_ref(
x: &Rational,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
assert_ne!(prec, 0);
if *x == 0u32 {
return (Self::ZERO, Equal);
}
tanh_rational_helper(x, prec, rm)
}
#[allow(clippy::needless_pass_by_value)]
#[inline]
pub fn tanh_rational_prec(x: Rational, prec: u64) -> (Self, Ordering) {
Self::tanh_rational_prec_round_ref(&x, prec, Nearest)
}
#[inline]
pub fn tanh_rational_prec_ref(x: &Rational, prec: u64) -> (Self, Ordering) {
Self::tanh_rational_prec_round_ref(x, prec, Nearest)
}
}
impl Tanh for Float {
type Output = Self;
#[inline]
fn tanh(self) -> Self {
let prec = self.significant_bits();
self.tanh_prec_round(prec, Nearest).0
}
}
impl Tanh for &Float {
type Output = Float;
#[inline]
fn tanh(self) -> Float {
self.tanh_prec_round_ref(self.significant_bits(), Nearest).0
}
}
impl TanhAssign for Float {
#[inline]
fn tanh_assign(&mut self) {
let prec = self.significant_bits();
self.tanh_prec_round_assign(prec, Nearest);
}
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_tanh<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::tanh_prec, x)
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_tanh_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::tanh_rational_prec_ref, x)
}