use crate::InnerFloat::{Finite, Infinity, NaN, Zero};
use crate::float::arithmetic::cosh::{
cosh_rational_helper, hyperbolic_approx, hyperbolic_can_round,
};
use crate::float::arithmetic::round_near_x::small_input_shortcut;
use crate::float::arithmetic::sinh::sinh_rational_helper;
use crate::float::conversion::string::set_str::overflow;
use crate::{
Float, emulate_float_to_float_pair_fn, emulate_rational_to_float_pair_fn, floor_and_ceiling,
};
use core::cmp::Ordering::{self, Equal};
use malachite_base::num::arithmetic::traits::{Abs, CeilingLogBase2, SinhCosh, SinhCoshAssign};
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, 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::platform::Limb;
use malachite_q::Rational;
fn sinh_cosh_prec_round_normal_ref(
x: &Float,
prec: u64,
rm: RoundingMode,
) -> (Float, Float, Ordering, Ordering) {
assert_ne!(rm, Exact, "Inexact sinh_cosh");
let exp_x = i64::from(x.get_exponent().unwrap());
let neg_two_exp = -(exp_x << 1);
if let Some((c, o_c)) = small_input_shortcut(&Float::ONE, neg_two_exp, 0, true, prec, rm)
&& let Some((s, o_s)) = small_input_shortcut(x, neg_two_exp, 2, true, prec, rm)
{
return (s, c, o_s, o_c);
}
let positive = x.is_sign_positive();
let x_abs = x.abs();
let mut working_prec = prec + prec.ceiling_log_base_2() + 4;
if exp_x < 0 {
working_prec += u64::exact_from(neg_two_exp);
}
let mut increment = Limb::WIDTH;
loop {
let Some(approx) = hyperbolic_approx(&x_abs, working_prec) else {
let (s, o_s) = overflow(positive, prec, rm);
let (c, o_c) = overflow(true, prec, rm);
return (s, c, o_s, o_c);
};
if hyperbolic_can_round(&approx.sinh, approx.sinh_bits, prec, rm)
&& hyperbolic_can_round(&approx.cosh, approx.cosh_bits, prec, rm)
{
let sinh_abs = approx.sinh;
let (s, o_s) =
Float::from_float_prec_round(if positive { sinh_abs } else { -sinh_abs }, prec, rm);
let (c, o_c) = Float::from_float_prec_round(approx.cosh, prec, rm);
return (s, c, o_s, o_c);
}
working_prec += increment;
increment = working_prec >> 1;
}
}
fn sinh_cosh_rational_helper(
x: &Rational,
prec: u64,
rm: RoundingMode,
) -> (Float, Float, Ordering, Ordering) {
assert_ne!(rm, Exact, "Inexact sinh_cosh");
let exp_x = x.floor_log_base_2_abs() + 1; if exp_x < -1 && u64::exact_from(-exp_x) << 4 >= prec + 10 {
let (s, o_s) = sinh_rational_helper(x, prec, rm);
let (c, o_c) = cosh_rational_helper(x, prec, rm);
return (s, c, o_s, o_c);
}
if exp_x >= Float::MAX_EXPONENT_I64 {
let (s, o_s) = overflow(*x > 0u32, prec, rm);
let (c, o_c) = overflow(true, prec, rm);
return (s, c, o_s, o_c);
}
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 sinh_cosh_prec_round_normal_ref(&x_lo, prec, rm);
}
let (x_lo, x_hi) = floor_and_ceiling((x_lo, x_o));
let (s_lo, c_lo, o_s_lo, o_c_lo) = sinh_cosh_prec_round_normal_ref(&x_lo, prec, rm);
let (s_hi, c_hi, o_s_hi, o_c_hi) = sinh_cosh_prec_round_normal_ref(&x_hi, prec, rm);
if o_s_lo == o_s_hi && o_c_lo == o_c_hi && s_lo == s_hi && c_lo == c_hi {
return (s_lo, c_lo, o_s_lo, o_c_lo);
}
working_prec += increment;
increment = working_prec >> 1;
}
}
impl Float {
#[inline]
pub fn sinh_cosh_prec_round(
self,
prec: u64,
rm: RoundingMode,
) -> (Self, Self, Ordering, Ordering) {
self.sinh_cosh_prec_round_ref(prec, rm)
}
pub fn sinh_cosh_prec_round_ref(
&self,
prec: u64,
rm: RoundingMode,
) -> (Self, Self, Ordering, Ordering) {
assert_ne!(prec, 0);
match &self.0 {
NaN => (Self::NAN, Self::NAN, Equal, Equal),
Infinity { .. } => (self.clone(), Self::INFINITY, Equal, Equal),
Zero { .. } => (self.clone(), Self::one_prec(prec), Equal, Equal),
Finite { .. } => sinh_cosh_prec_round_normal_ref(self, prec, rm),
}
}
#[inline]
pub fn sinh_cosh_prec(self, prec: u64) -> (Self, Self, Ordering, Ordering) {
self.sinh_cosh_prec_round_ref(prec, Nearest)
}
#[inline]
pub fn sinh_cosh_prec_ref(&self, prec: u64) -> (Self, Self, Ordering, Ordering) {
self.sinh_cosh_prec_round_ref(prec, Nearest)
}
#[inline]
pub fn sinh_cosh_round(self, rm: RoundingMode) -> (Self, Self, Ordering, Ordering) {
let prec = self.significant_bits();
self.sinh_cosh_prec_round_ref(prec, rm)
}
#[inline]
pub fn sinh_cosh_round_ref(&self, rm: RoundingMode) -> (Self, Self, Ordering, Ordering) {
self.sinh_cosh_prec_round_ref(self.significant_bits(), rm)
}
#[inline]
pub fn sinh_cosh_prec_round_assign(
&mut self,
cosh: &mut Self,
prec: u64,
rm: RoundingMode,
) -> (Ordering, Ordering) {
let (s, c, o_s, o_c) = self.sinh_cosh_prec_round_ref(prec, rm);
*self = s;
*cosh = c;
(o_s, o_c)
}
#[inline]
pub fn sinh_cosh_prec_assign(&mut self, cosh: &mut Self, prec: u64) -> (Ordering, Ordering) {
self.sinh_cosh_prec_round_assign(cosh, prec, Nearest)
}
#[inline]
pub fn sinh_cosh_round_assign(
&mut self,
cosh: &mut Self,
rm: RoundingMode,
) -> (Ordering, Ordering) {
let prec = self.significant_bits();
self.sinh_cosh_prec_round_assign(cosh, prec, rm)
}
}
impl Float {
#[allow(clippy::needless_pass_by_value)]
#[inline]
pub fn sinh_cosh_rational_prec_round(
x: Rational,
prec: u64,
rm: RoundingMode,
) -> (Self, Self, Ordering, Ordering) {
Self::sinh_cosh_rational_prec_round_ref(&x, prec, rm)
}
pub fn sinh_cosh_rational_prec_round_ref(
x: &Rational,
prec: u64,
rm: RoundingMode,
) -> (Self, Self, Ordering, Ordering) {
assert_ne!(prec, 0);
if *x == 0u32 {
return (Self::ZERO, Self::one_prec(prec), Equal, Equal);
}
sinh_cosh_rational_helper(x, prec, rm)
}
#[allow(clippy::needless_pass_by_value)]
#[inline]
pub fn sinh_cosh_rational_prec(x: Rational, prec: u64) -> (Self, Self, Ordering, Ordering) {
Self::sinh_cosh_rational_prec_round_ref(&x, prec, Nearest)
}
#[inline]
pub fn sinh_cosh_rational_prec_ref(
x: &Rational,
prec: u64,
) -> (Self, Self, Ordering, Ordering) {
Self::sinh_cosh_rational_prec_round_ref(x, prec, Nearest)
}
}
impl SinhCosh for Float {
type Output = Self;
#[inline]
fn sinh_cosh(self) -> (Self, Self) {
let prec = self.significant_bits();
let (s, c, _, _) = self.sinh_cosh_prec_round_ref(prec, Nearest);
(s, c)
}
}
impl SinhCosh for &Float {
type Output = Float;
#[inline]
fn sinh_cosh(self) -> (Float, Float) {
let (s, c, _, _) = self.sinh_cosh_prec_round_ref(self.significant_bits(), Nearest);
(s, c)
}
}
impl SinhCoshAssign for Float {
#[inline]
fn sinh_cosh_assign(&mut self, cosh: &mut Self) {
let prec = self.significant_bits();
self.sinh_cosh_prec_round_assign(cosh, prec, Nearest);
}
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_sinh_cosh<T: PrimitiveFloat>(x: T) -> (T, T)
where
Float: From<T> + PartialOrd<T>,
for<'a> T: ExactFrom<&'a Float> + RoundingFrom<&'a Float>,
{
emulate_float_to_float_pair_fn(Float::sinh_cosh_prec, x)
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_sinh_cosh_rational<T: PrimitiveFloat>(x: &Rational) -> (T, T)
where
Float: PartialOrd<T>,
for<'a> T: ExactFrom<&'a Float> + RoundingFrom<&'a Float>,
{
emulate_rational_to_float_pair_fn(Float::sinh_cosh_rational_prec_ref, x)
}