use crate::InnerFloat::{Finite, Infinity, NaN, Zero};
use crate::float::arithmetic::cos::round_bracket;
use crate::float::arithmetic::exp::one_neighbor;
use crate::float::arithmetic::round_near_x::small_input_shortcut;
use crate::float::conversion::string::set_str::overflow;
use crate::{Float, emulate_float_to_float_fn, emulate_rational_to_float_fn, floor_and_ceiling};
use core::cmp::Ordering::{self, Equal, Greater, Less};
use core::cmp::max;
use malachite_base::fail_on_untested_path;
use malachite_base::num::arithmetic::traits::{
Abs, AddMul, CeilingLogBase2, Cosh, CoshAssign, Reciprocal, ShrRound, 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, One};
use malachite_base::num::conversion::traits::{ExactFrom, RoundingFrom};
use malachite_base::num::logic::traits::{CountOnes, 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;
pub(crate) fn is_max_finite(x: &Float) -> bool {
x.get_exponent() == Some(Float::MAX_EXPONENT)
&& x.significand_ref().unwrap().count_ones() == x.get_prec().unwrap()
}
fn half_exp_near_overflow(x: &Float, working_prec: u64) -> Option<Float> {
let u = (x >> 1u32).exp_prec_round(working_prec, Floor).0;
if u.get_exponent() == Some(Float::MAX_EXPONENT) {
return None;
}
let h = (&u >> 1u32).mul_round(u, Floor).0; if is_max_finite(&h) {
return None;
}
Some(h)
}
fn half_exp(x: &Float, working_prec: u64) -> Option<(Float, bool)> {
let exp_x = x.exp_prec_round_ref(working_prec, Floor).0;
if exp_x.get_exponent() == Some(Float::MAX_EXPONENT) {
half_exp_near_overflow(x, working_prec).map(|h| (h, true))
} else {
Some((exp_x >> 1u32, false))
}
}
pub(crate) struct HyperbolicApprox {
pub sinh: Float,
pub sinh_bits: u64,
pub cosh: Float,
pub cosh_bits: u64,
}
pub(crate) fn hyperbolic_approx(x: &Float, working_prec: u64) -> Option<HyperbolicApprox> {
let (h, near_overflow) = half_exp(x, working_prec)?;
let exp_neg_x_half = h.reciprocal_round_ref(Ceiling).0.shr_round(2u32, Ceiling).0;
let exp_h = i64::from(h.get_exponent().unwrap());
let sinh = &h - &exp_neg_x_half;
let cosh = h.add_round(exp_neg_x_half, Ceiling).0;
let sinh_bits = if sinh == 0u32 {
0
} else {
let d = exp_h - i64::from(sinh.get_exponent().unwrap()) + 3;
let loss = u64::exact_from(max(d, 0)) + if near_overflow { 4 } else { 1 };
working_prec.saturating_sub(loss)
};
let cosh_bits = working_prec - if near_overflow { 4 } else { 3 };
Some(HyperbolicApprox {
sinh,
sinh_bits,
cosh,
cosh_bits,
})
}
pub(crate) fn hyperbolic_can_round(f: &Float, bits: u64, prec: u64, rm: RoundingMode) -> bool {
bits != 0 && float_can_round(f.significand_ref().unwrap(), bits, prec, rm)
}
fn cosh_prec_round_normal_ref(x: &Float, prec: u64, rm: RoundingMode) -> (Float, Ordering) {
assert_ne!(rm, Exact, "Inexact cosh");
let exp_x = i64::from(x.get_exponent().unwrap());
if let Some(result) = small_input_shortcut(&Float::ONE, -(exp_x << 1), 0, true, prec, rm) {
return result;
}
let x = x.abs();
let mut working_prec = prec + 3 + prec.ceiling_log_base_2();
let mut increment = Limb::WIDTH;
loop {
let Some(approx) = hyperbolic_approx(&x, working_prec) else {
return overflow(true, prec, rm);
};
if hyperbolic_can_round(&approx.cosh, approx.cosh_bits, prec, rm) {
return Float::from_float_prec_round(approx.cosh, prec, rm);
}
working_prec += increment;
increment = working_prec >> 1;
}
}
fn cosh_rational_series(x: &Rational, prec: u64, rm: RoundingMode) -> (Float, Ordering) {
fail_on_untested_path("cosh_rational_series");
let x_squared = x.square();
let tail_factor = (Rational::ONE - &x_squared).reciprocal();
let mut s = Rational::ONE;
let mut term = Rational::ONE;
let mut k = 1u64;
loop {
term *= &x_squared;
term /= Rational::from((k << 1) * ((k << 1) - 1));
let hi = (&s).add_mul(&term, &tail_factor);
if let Some(result) = round_bracket(&s, &hi, prec, rm) {
return result;
}
s += &term;
k += 1;
}
}
pub(crate) fn cosh_rational_helper(x: &Rational, prec: u64, rm: RoundingMode) -> (Float, Ordering) {
assert_ne!(rm, Exact, "Inexact cosh");
let exp_x = x.floor_log_base_2_abs() + 1; if -(exp_x << 1) >= i64::exact_from(prec) {
return match rm {
Ceiling | Up => (one_neighbor(prec, true), Greater),
_ => (Float::one_prec(prec), Less),
};
}
if exp_x <= Float::MIN_EXPONENT_I64 {
return cosh_rational_series(x, prec, rm);
}
if exp_x >= Float::MAX_EXPONENT_I64 {
return overflow(true, prec, rm);
}
let x_abs = x.abs();
monotone_rational_via_floats(&x_abs, prec, rm, cosh_prec_round_normal_ref)
}
pub(crate) fn same_rounding(
(y_lo, o_lo): (Float, Ordering),
(y_hi, o_hi): (Float, Ordering),
) -> Option<(Float, Ordering)> {
(o_lo == o_hi && o_lo != Equal && y_lo == y_hi).then_some((y_lo, o_lo))
}
pub(crate) fn monotone_rational_via_floats<
F: Fn(&Float, u64, RoundingMode) -> (Float, Ordering),
>(
x: &Rational,
prec: u64,
rm: RoundingMode,
f: F,
) -> (Float, Ordering) {
let mut working_prec = prec + 10;
let mut increment = Limb::WIDTH;
loop {
let (x_lo, x_o) = Float::from_rational_prec_round_ref(x, working_prec, Floor);
if x_o == Equal {
return f(&x_lo, prec, rm);
}
let (x_lo, x_hi) = floor_and_ceiling((x_lo, x_o));
if let Some(result) = same_rounding(f(&x_lo, prec, rm), f(&x_hi, prec, rm)) {
return result;
}
working_prec += increment;
increment = working_prec >> 1;
}
}
impl Float {
#[inline]
pub fn cosh_prec_round(self, prec: u64, rm: RoundingMode) -> (Self, Ordering) {
self.cosh_prec_round_ref(prec, rm)
}
pub fn cosh_prec_round_ref(&self, prec: u64, rm: RoundingMode) -> (Self, Ordering) {
assert_ne!(prec, 0);
match &self.0 {
NaN => (Self::NAN, Equal),
Infinity { .. } => (Self::INFINITY, Equal),
Zero { .. } => (Self::one_prec(prec), Equal),
Finite { .. } => cosh_prec_round_normal_ref(self, prec, rm),
}
}
#[inline]
pub fn cosh_prec(self, prec: u64) -> (Self, Ordering) {
self.cosh_prec_round(prec, Nearest)
}
#[inline]
pub fn cosh_prec_ref(&self, prec: u64) -> (Self, Ordering) {
self.cosh_prec_round_ref(prec, Nearest)
}
#[inline]
pub fn cosh_round(self, rm: RoundingMode) -> (Self, Ordering) {
let prec = self.significant_bits();
self.cosh_prec_round(prec, rm)
}
#[inline]
pub fn cosh_round_ref(&self, rm: RoundingMode) -> (Self, Ordering) {
self.cosh_prec_round_ref(self.significant_bits(), rm)
}
#[inline]
pub fn cosh_prec_round_assign(&mut self, prec: u64, rm: RoundingMode) -> Ordering {
let o;
(*self, o) = self.cosh_prec_round_ref(prec, rm);
o
}
#[inline]
pub fn cosh_prec_assign(&mut self, prec: u64) -> Ordering {
self.cosh_prec_round_assign(prec, Nearest)
}
#[inline]
pub fn cosh_round_assign(&mut self, rm: RoundingMode) -> Ordering {
let prec = self.significant_bits();
self.cosh_prec_round_assign(prec, rm)
}
}
impl Float {
#[allow(clippy::needless_pass_by_value)]
#[inline]
pub fn cosh_rational_prec_round(x: Rational, prec: u64, rm: RoundingMode) -> (Self, Ordering) {
Self::cosh_rational_prec_round_ref(&x, prec, rm)
}
pub fn cosh_rational_prec_round_ref(
x: &Rational,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
assert_ne!(prec, 0);
if *x == 0u32 {
return (Self::one_prec(prec), Equal);
}
cosh_rational_helper(x, prec, rm)
}
#[allow(clippy::needless_pass_by_value)]
#[inline]
pub fn cosh_rational_prec(x: Rational, prec: u64) -> (Self, Ordering) {
Self::cosh_rational_prec_round_ref(&x, prec, Nearest)
}
#[inline]
pub fn cosh_rational_prec_ref(x: &Rational, prec: u64) -> (Self, Ordering) {
Self::cosh_rational_prec_round_ref(x, prec, Nearest)
}
}
impl Cosh for Float {
type Output = Self;
#[inline]
fn cosh(self) -> Self {
let prec = self.significant_bits();
self.cosh_prec_round(prec, Nearest).0
}
}
impl Cosh for &Float {
type Output = Float;
#[inline]
fn cosh(self) -> Float {
self.cosh_prec_round_ref(self.significant_bits(), Nearest).0
}
}
impl CoshAssign for Float {
#[inline]
fn cosh_assign(&mut self) {
let prec = self.significant_bits();
self.cosh_prec_round_assign(prec, Nearest);
}
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_cosh<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::cosh_prec, x)
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_cosh_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::cosh_rational_prec_ref, x)
}