use crate::InnerFloat::{Finite, Infinity, NaN, Zero};
use crate::float::arithmetic::cos::{cos_rational_tiny, round_scaled_bracket};
use crate::float::arithmetic::cosh::monotone_rational_via_floats;
use crate::float::arithmetic::exp::one_neighbor;
use crate::float::arithmetic::round_near_x::small_input_shortcut;
use crate::float::arithmetic::sin::underflowed;
use crate::float::arithmetic::tan::round_bracket_signed_by;
use crate::float::arithmetic::tanh::cosh_bound;
use crate::{Float, emulate_float_to_float_fn, emulate_rational_to_float_fn, floor_and_ceiling};
use core::cmp::Ordering::{self, Equal};
use malachite_base::fail_on_untested_path;
use malachite_base::num::arithmetic::traits::{
Abs, CeilingLogBase2, Reciprocal, ReciprocalAssign, Sech, SechAssign,
};
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;
pub(crate) const RECIPROCAL_HYPERBOLIC_UNDERFLOW_THRESHOLD: u32 = 744261120;
fn reciprocal_hyperbolic_scaled(
x_abs: &Float,
negative: bool,
plus: bool,
prec: u64,
rm: RoundingMode,
) -> (Float, Ordering) {
let neg_half_x = -(x_abs >> 1u32);
let mut working_prec = prec + prec.ceiling_log_base_2() + 10;
let mut increment = Limb::WIDTH;
loop {
let (a_lo, a_hi) = floor_and_ceiling(neg_half_x.exp_prec_round_ref(working_prec, Floor));
let s = -i64::from(a_lo.get_exponent().unwrap());
let mut lo = (a_lo << s).square_round(Floor).0;
let mut hi = (a_hi << s).square_round(Ceiling).0;
let four_s = u64::exact_from(s << 2);
if working_prec + 2 < four_s {
if plus {
lo.decrement();
} else {
hi.increment();
}
} else {
fail_on_untested_path("reciprocal_hyperbolic_scaled, working precision beyond 4S");
if plus {
lo.mul_prec_round_assign(one_neighbor(four_s, false), working_prec, Floor);
} else {
hi.mul_prec_round_assign(one_neighbor(four_s - 1, true), working_prec, Ceiling);
}
}
let (lo, hi) = if negative { (-hi, -lo) } else { (lo, hi) };
if let Some(result) = round_scaled_bracket(&lo, &hi, 1 - (s << 1), prec, rm) {
return result;
}
working_prec += increment;
increment = working_prec >> 1;
}
}
pub(crate) fn reciprocal_hyperbolic_large(
x: &Float,
plus: bool,
prec: u64,
rm: RoundingMode,
) -> Option<(Float, Ordering)> {
if x.get_exponent().unwrap() <= 29 {
return None;
}
let negative = !plus && x.is_sign_negative();
let x_abs = x.abs();
Some(if x_abs >= RECIPROCAL_HYPERBOLIC_UNDERFLOW_THRESHOLD {
underflowed(!negative, prec, rm)
} else {
reciprocal_hyperbolic_scaled(&x_abs, negative, plus, prec, rm)
})
}
fn sech_prec_round_normal_ref(x: &Float, prec: u64, rm: RoundingMode) -> (Float, Ordering) {
assert_ne!(rm, Exact, "Inexact sech");
let exp_x = i64::from(x.get_exponent().unwrap());
if let Some(result) = small_input_shortcut(&Float::ONE, -(exp_x << 1), 1, false, prec, rm) {
return result;
}
if let Some(result) = reciprocal_hyperbolic_large(x, true, prec, rm) {
return result;
}
let mut working_prec = prec + prec.ceiling_log_base_2() + 3;
let mut increment = Limb::WIDTH;
loop {
let mut sech_x = x.cosh_prec_round_ref(working_prec, Down).0;
sech_x.reciprocal_assign();
if float_can_round(
sech_x.significand_ref().unwrap(),
working_prec - 2,
prec,
rm,
) {
return Float::from_float_prec_round(sech_x, prec, rm);
}
working_prec += increment;
increment = working_prec >> 1;
}
}
fn sech_rational_helper(x: &Rational, prec: u64, rm: RoundingMode) -> (Float, Ordering) {
assert_ne!(rm, Exact, "Inexact sech");
let exp_x = x.floor_log_base_2_abs() + 1; if 1 - (exp_x << 1) > i64::exact_from(prec) {
return cos_rational_tiny(prec, rm);
}
if exp_x < -1 && u64::exact_from(-exp_x) << 4 >= prec + 10 {
return hyperbolic_series_quotient(x, false, None, cosh_bound, prec, rm);
}
let x_abs = x.abs();
if x_abs >= RECIPROCAL_HYPERBOLIC_UNDERFLOW_THRESHOLD {
return underflowed(true, prec, rm);
}
monotone_rational_via_floats(&x_abs, prec, rm, sech_prec_round_normal_ref)
}
pub(crate) type HyperbolicBound = fn(&Rational, u64, bool) -> Rational;
pub(crate) fn hyperbolic_series_quotient(
x: &Rational,
negative: bool,
numerator: Option<HyperbolicBound>,
denominator: HyperbolicBound,
prec: u64,
rm: RoundingMode,
) -> (Float, Ordering) {
let ax = x.abs();
let mut w = prec + 10;
let mut increment = Limb::WIDTH;
loop {
let d_lo = denominator(&ax, w, false);
let d_hi = denominator(&ax, w, true);
let (lo, hi) = match numerator {
None => (d_hi.reciprocal(), d_lo.reciprocal()),
Some(numerator) => (
numerator(&ax, w, false) / d_hi,
numerator(&ax, w, true) / d_lo,
),
};
if let Some(result) = round_bracket_signed_by(negative, lo, hi, prec, rm) {
return result;
}
w += increment;
increment = w >> 1;
}
}
impl Float {
#[inline]
pub fn sech_prec_round(self, prec: u64, rm: RoundingMode) -> (Self, Ordering) {
self.sech_prec_round_ref(prec, rm)
}
pub fn sech_prec_round_ref(&self, prec: u64, rm: RoundingMode) -> (Self, Ordering) {
assert_ne!(prec, 0);
match &self.0 {
NaN => (Self::NAN, Equal),
Infinity { .. } => (Self::ZERO, Equal),
Zero { .. } => (Self::one_prec(prec), Equal),
Finite { .. } => sech_prec_round_normal_ref(self, prec, rm),
}
}
#[inline]
pub fn sech_prec(self, prec: u64) -> (Self, Ordering) {
self.sech_prec_round(prec, Nearest)
}
#[inline]
pub fn sech_prec_ref(&self, prec: u64) -> (Self, Ordering) {
self.sech_prec_round_ref(prec, Nearest)
}
#[inline]
pub fn sech_round(self, rm: RoundingMode) -> (Self, Ordering) {
let prec = self.significant_bits();
self.sech_prec_round(prec, rm)
}
#[inline]
pub fn sech_round_ref(&self, rm: RoundingMode) -> (Self, Ordering) {
self.sech_prec_round_ref(self.significant_bits(), rm)
}
#[inline]
pub fn sech_prec_round_assign(&mut self, prec: u64, rm: RoundingMode) -> Ordering {
let o;
(*self, o) = self.sech_prec_round_ref(prec, rm);
o
}
#[inline]
pub fn sech_prec_assign(&mut self, prec: u64) -> Ordering {
self.sech_prec_round_assign(prec, Nearest)
}
#[inline]
pub fn sech_round_assign(&mut self, rm: RoundingMode) -> Ordering {
let prec = self.significant_bits();
self.sech_prec_round_assign(prec, rm)
}
}
impl Float {
#[allow(clippy::needless_pass_by_value)]
#[inline]
pub fn sech_rational_prec_round(x: Rational, prec: u64, rm: RoundingMode) -> (Self, Ordering) {
Self::sech_rational_prec_round_ref(&x, prec, rm)
}
pub fn sech_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);
}
sech_rational_helper(x, prec, rm)
}
#[allow(clippy::needless_pass_by_value)]
#[inline]
pub fn sech_rational_prec(x: Rational, prec: u64) -> (Self, Ordering) {
Self::sech_rational_prec_round_ref(&x, prec, Nearest)
}
#[inline]
pub fn sech_rational_prec_ref(x: &Rational, prec: u64) -> (Self, Ordering) {
Self::sech_rational_prec_round_ref(x, prec, Nearest)
}
}
impl Sech for Float {
type Output = Self;
#[inline]
fn sech(self) -> Self {
let prec = self.significant_bits();
self.sech_prec_round(prec, Nearest).0
}
}
impl Sech for &Float {
type Output = Float;
#[inline]
fn sech(self) -> Float {
self.sech_prec_round_ref(self.significant_bits(), Nearest).0
}
}
impl SechAssign for Float {
#[inline]
fn sech_assign(&mut self) {
let prec = self.significant_bits();
self.sech_prec_round_assign(prec, Nearest);
}
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_sech<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::sech_prec, x)
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_sech_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::sech_rational_prec_ref, x)
}