use crate::Float;
use crate::InnerFloat::{Finite, Infinity, NaN, Zero};
use crate::float::MAX_EXPONENT_I64;
use crate::float::arithmetic::atan::{arc_with_period_scale, scaled_unsigned};
use crate::float::arithmetic::round_near_x::{round_from_below, value_is_tie};
use crate::float::arithmetic::sin::{SCALE, SCALE_I64, SCALED_INPUT_EXPONENT, scaled_underflow};
use crate::{emulate_float_to_float_fn, emulate_rational_to_float_fn};
use core::cmp::Ordering::{self, Equal, Greater, Less};
use core::cmp::max;
use malachite_base::num::arithmetic::traits::{
Abs, Acsc, AcscAssign, Atan, CeilingLogBase2, IsPowerOf2, PowerOf2, Reciprocal, Square,
};
use malachite_base::num::basic::floats::PrimitiveFloat;
use malachite_base::num::basic::integers::PrimitiveInt;
use malachite_base::num::basic::traits::{NaN as NaNTrait, NegativeZero, One, Zero as ZeroTrait};
use malachite_base::num::comparison::traits::PartialOrdAbs;
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) fn signed_half_pi(negative: bool, prec: u64, rm: RoundingMode) -> (Float, Ordering) {
let (pi, o) = Float::pi_prec_round(prec, if negative { -rm } else { rm });
let half = pi >> 1u32;
if negative {
(-half, o.reverse())
} else {
(half, o)
}
}
fn acsc_abs_prec_round(x: &Float, prec: u64, rm: RoundingMode) -> (Float, Ordering) {
let exp_x = i64::from(x.get_exponent().unwrap());
if exp_x << 1 > MAX_EXPONENT_I64
|| exp_x << 1 > i64::exact_from(prec + x.get_prec().unwrap()) + 4
{
let a = x.abs();
let tie = rm == Nearest && {
let (wide, o_wide) = a.reciprocal_prec_ref(prec + 1);
value_is_tie(&wide, o_wide, prec)
};
let (t, o) = a.reciprocal_prec_round(prec, rm);
return round_from_below(t, o, tie, rm);
}
let exact_w = (x.get_prec().unwrap() << 1) + 2;
let mut w = prec + prec.ceiling_log_base_2() + 10;
let mut increment = Limb::WIDTH;
loop {
let q = x
.square_prec_ref(max(w, exact_w))
.0
.sub_prec(Float::ONE, max(w, exact_w))
.0
.reciprocal_sqrt_prec(w)
.0;
let t = q.atan();
if float_can_round(t.significand_ref().unwrap(), w - 3, prec, rm) {
return Float::from_float_prec_round(t, prec, rm);
}
w += increment;
increment = w >> 1;
}
}
fn acsc_prec_round_normal_ref(x: &Float, prec: u64, rm: RoundingMode) -> (Float, Ordering) {
let negative = *x < 0u32;
match x.partial_cmp_abs(&1u32).unwrap() {
Less => (Float::NAN, Equal),
Equal => {
assert_ne!(rm, Exact, "Inexact acsc");
signed_half_pi(negative, prec, rm)
}
Greater => {
assert_ne!(rm, Exact, "Inexact acsc");
let (t, o) = acsc_abs_prec_round(x, prec, if negative { -rm } else { rm });
if negative { (-t, o.reverse()) } else { (t, o) }
}
}
}
pub(crate) fn acsc_rational_helper(x: &Rational, prec: u64, rm: RoundingMode) -> (Float, Ordering) {
assert_ne!(rm, Exact, "Inexact acsc_rational");
let negative = *x < 0u32;
let rm = if negative { -rm } else { rm };
let exp_x = x.floor_log_base_2_abs() + 1;
let xp = x.abs();
let mut w = prec + prec.ceiling_log_base_2() + 10;
let mut increment = Limb::WIDTH;
if 1 - exp_x <= SCALED_INPUT_EXPONENT {
let scaled = Rational::power_of_2(SCALE_I64) / &xp;
loop {
let t = Float::from_rational_prec_round_ref(&scaled, w, Up).0;
if let Some((t, o)) = scaled_underflow(&t, true, prec, rm) {
return if negative { (-t, o.reverse()) } else { (t, o) };
}
let t = t >> SCALE;
if float_can_round(t.significand_ref().unwrap(), w - 2, prec, rm) {
let (t, o) = Float::from_float_prec_round(t, prec, rm);
return if negative { (-t, o.reverse()) } else { (t, o) };
}
w += increment;
increment = w >> 1;
}
}
if exp_x << 1 > MAX_EXPONENT_I64
|| exp_x << 1 > i64::exact_from(prec + xp.numerator_ref().significant_bits()) + 4
{
let recip = (&xp).reciprocal();
let tie = rm == Nearest && {
let (wide, o_wide) = Float::from_rational_prec_ref(&recip, prec + 1);
value_is_tie(&wide, o_wide, prec)
};
let (t, o) = Float::from_rational_prec_round(recip, prec, rm);
let (t, o) = round_from_below(t, o, tie, rm);
return if negative { (-t, o.reverse()) } else { (t, o) };
}
let mut r = None;
loop {
let r = r.get_or_insert_with(|| (&xp).square() - Rational::ONE);
let q = Float::reciprocal_sqrt_rational_prec_ref(r, w).0;
let t = q.atan();
if float_can_round(t.significand_ref().unwrap(), w - 3, prec, rm) {
let (t, o) = Float::from_float_prec_round(t, prec, rm);
return if negative { (-t, o.reverse()) } else { (t, o) };
}
w += increment;
increment = w >> 1;
}
}
fn acsc_with_period_prec_round_normal_ref(
x: &Float,
u: u64,
prec: u64,
rm: RoundingMode,
) -> (Float, Ordering) {
let positive = *x > 0u32;
let exp_x = i64::from(x.get_exponent().unwrap());
let power_of_2 = x.significand_ref().unwrap().is_power_of_2();
if exp_x == 1 && power_of_2 {
return scaled_unsigned(u, 2, positive, prec, rm);
}
if exp_x == 2 && power_of_2 && u.is_multiple_of(3) {
return scaled_unsigned(u / 3, 2, positive, prec, rm);
}
assert_ne!(rm, Exact, "Inexact acsc_with_period");
arc_with_period_scale(
|w| x.acsc_prec_round_ref(w, Up).0 << SCALE,
u,
positive,
prec,
rm,
)
}
pub(crate) fn acsc_with_period_rational_helper(
x: &Rational,
u: u64,
prec: u64,
rm: RoundingMode,
) -> (Float, Ordering) {
let positive = *x > 0u32;
let exp_x = x.floor_log_base_2_abs() + 1;
let integer = x.denominator_ref() == &1u32;
if integer && x.numerator_ref() == &1u32 {
return scaled_unsigned(u, 2, positive, prec, rm);
}
if integer && x.numerator_ref() == &2u32 && u.is_multiple_of(3) {
return scaled_unsigned(u / 3, 2, positive, prec, rm);
}
assert_ne!(rm, Exact, "Inexact acsc_with_period_rational");
if 1 - exp_x <= SCALED_INPUT_EXPONENT {
let scaled = Rational::power_of_2(SCALE_I64) / x;
return arc_with_period_scale(
|w| Float::from_rational_prec_round_ref(&scaled, w, Up).0,
u,
positive,
prec,
rm,
);
}
arc_with_period_scale(
|w| acsc_rational_helper(x, w, Up).0 << SCALE,
u,
positive,
prec,
rm,
)
}
impl Float {
#[inline]
pub fn acsc_prec_round(self, prec: u64, rm: RoundingMode) -> (Self, Ordering) {
self.acsc_prec_round_ref(prec, rm)
}
pub fn acsc_prec_round_ref(&self, prec: u64, rm: RoundingMode) -> (Self, Ordering) {
assert_ne!(prec, 0);
match &self.0 {
NaN | Zero { .. } => (Self::NAN, Equal),
Infinity { sign } => (
if *sign {
Self::ZERO
} else {
Self::NEGATIVE_ZERO
},
Equal,
),
Finite { .. } => acsc_prec_round_normal_ref(self, prec, rm),
}
}
#[inline]
pub fn acsc_prec(self, prec: u64) -> (Self, Ordering) {
self.acsc_prec_round(prec, Nearest)
}
#[inline]
pub fn acsc_prec_ref(&self, prec: u64) -> (Self, Ordering) {
self.acsc_prec_round_ref(prec, Nearest)
}
#[inline]
pub fn acsc_round(self, rm: RoundingMode) -> (Self, Ordering) {
let prec = self.significant_bits();
self.acsc_prec_round(prec, rm)
}
#[inline]
pub fn acsc_round_ref(&self, rm: RoundingMode) -> (Self, Ordering) {
self.acsc_prec_round_ref(self.significant_bits(), rm)
}
#[inline]
pub fn acsc_prec_round_assign(&mut self, prec: u64, rm: RoundingMode) -> Ordering {
let (s, o) = self.acsc_prec_round_ref(prec, rm);
*self = s;
o
}
#[inline]
pub fn acsc_prec_assign(&mut self, prec: u64) -> Ordering {
self.acsc_prec_round_assign(prec, Nearest)
}
#[inline]
pub fn acsc_round_assign(&mut self, rm: RoundingMode) -> Ordering {
self.acsc_prec_round_assign(self.significant_bits(), rm)
}
#[inline]
#[allow(clippy::needless_pass_by_value)]
pub fn acsc_rational_prec_round(x: Rational, prec: u64, rm: RoundingMode) -> (Self, Ordering) {
Self::acsc_rational_prec_round_ref(&x, prec, rm)
}
pub fn acsc_rational_prec_round_ref(
x: &Rational,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
assert_ne!(prec, 0);
match x.partial_cmp_abs(&1u32).unwrap() {
Less => (Self::NAN, Equal),
Equal => {
assert_ne!(rm, Exact, "Inexact acsc_rational");
signed_half_pi(*x < 0u32, prec, rm)
}
Greater => acsc_rational_helper(x, prec, rm),
}
}
#[inline]
pub fn acsc_rational_prec(x: Rational, prec: u64) -> (Self, Ordering) {
Self::acsc_rational_prec_round(x, prec, Nearest)
}
#[inline]
pub fn acsc_rational_prec_ref(x: &Rational, prec: u64) -> (Self, Ordering) {
Self::acsc_rational_prec_round_ref(x, prec, Nearest)
}
#[inline]
pub fn acsc_with_period_prec_round(
self,
u: u64,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
self.acsc_with_period_prec_round_ref(u, prec, rm)
}
pub fn acsc_with_period_prec_round_ref(
&self,
u: u64,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
assert_ne!(prec, 0);
match &self.0 {
NaN | Zero { .. } => (Self::NAN, Equal),
Infinity { sign } => (
if *sign {
Self::ZERO
} else {
Self::NEGATIVE_ZERO
},
Equal,
),
Finite { sign, .. } => {
if self.lt_abs(&1u32) {
(Self::NAN, Equal)
} else if u == 0 {
(
if *sign {
Self::ZERO
} else {
Self::NEGATIVE_ZERO
},
Equal,
)
} else {
acsc_with_period_prec_round_normal_ref(self, u, prec, rm)
}
}
}
}
#[inline]
pub fn acsc_with_period_prec(self, u: u64, prec: u64) -> (Self, Ordering) {
self.acsc_with_period_prec_round(u, prec, Nearest)
}
#[inline]
pub fn acsc_with_period_prec_ref(&self, u: u64, prec: u64) -> (Self, Ordering) {
self.acsc_with_period_prec_round_ref(u, prec, Nearest)
}
#[inline]
pub fn acsc_with_period_round(self, u: u64, rm: RoundingMode) -> (Self, Ordering) {
let prec = self.significant_bits();
self.acsc_with_period_prec_round(u, prec, rm)
}
#[inline]
pub fn acsc_with_period_round_ref(&self, u: u64, rm: RoundingMode) -> (Self, Ordering) {
self.acsc_with_period_prec_round_ref(u, self.significant_bits(), rm)
}
#[inline]
pub fn acsc_with_period(self, u: u64) -> Self {
let prec = self.significant_bits();
self.acsc_with_period_prec(u, prec).0
}
#[inline]
pub fn acsc_with_period_ref(&self, u: u64) -> Self {
self.acsc_with_period_prec_ref(u, self.significant_bits()).0
}
#[inline]
pub fn acsc_with_period_prec_round_assign(
&mut self,
u: u64,
prec: u64,
rm: RoundingMode,
) -> Ordering {
let (s, o) = self.acsc_with_period_prec_round_ref(u, prec, rm);
*self = s;
o
}
#[inline]
pub fn acsc_with_period_prec_assign(&mut self, u: u64, prec: u64) -> Ordering {
self.acsc_with_period_prec_round_assign(u, prec, Nearest)
}
#[inline]
pub fn acsc_with_period_round_assign(&mut self, u: u64, rm: RoundingMode) -> Ordering {
self.acsc_with_period_prec_round_assign(u, self.significant_bits(), rm)
}
#[inline]
pub fn acsc_with_period_assign(&mut self, u: u64) {
let prec = self.significant_bits();
self.acsc_with_period_prec_assign(u, prec);
}
#[inline]
#[allow(clippy::needless_pass_by_value)]
pub fn acsc_with_period_rational_prec_round(
x: Rational,
u: u64,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
Self::acsc_with_period_rational_prec_round_ref(&x, u, prec, rm)
}
pub fn acsc_with_period_rational_prec_round_ref(
x: &Rational,
u: u64,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
assert_ne!(prec, 0);
if x.lt_abs(&1u32) {
return (Self::NAN, Equal);
}
if u == 0 {
return (
if *x > 0u32 {
Self::ZERO
} else {
Self::NEGATIVE_ZERO
},
Equal,
);
}
acsc_with_period_rational_helper(x, u, prec, rm)
}
#[inline]
pub fn acsc_with_period_rational_prec(x: Rational, u: u64, prec: u64) -> (Self, Ordering) {
Self::acsc_with_period_rational_prec_round(x, u, prec, Nearest)
}
#[inline]
pub fn acsc_with_period_rational_prec_ref(x: &Rational, u: u64, prec: u64) -> (Self, Ordering) {
Self::acsc_with_period_rational_prec_round_ref(x, u, prec, Nearest)
}
#[inline]
pub fn acsc_pi_prec_round(self, prec: u64, rm: RoundingMode) -> (Self, Ordering) {
self.acsc_with_period_prec_round(2, prec, rm)
}
#[inline]
pub fn acsc_pi_prec_round_ref(&self, prec: u64, rm: RoundingMode) -> (Self, Ordering) {
self.acsc_with_period_prec_round_ref(2, prec, rm)
}
#[inline]
pub fn acsc_pi_prec(self, prec: u64) -> (Self, Ordering) {
self.acsc_with_period_prec(2, prec)
}
#[inline]
pub fn acsc_pi_prec_ref(&self, prec: u64) -> (Self, Ordering) {
self.acsc_with_period_prec_ref(2, prec)
}
#[inline]
pub fn acsc_pi_round(self, rm: RoundingMode) -> (Self, Ordering) {
self.acsc_with_period_round(2, rm)
}
#[inline]
pub fn acsc_pi_round_ref(&self, rm: RoundingMode) -> (Self, Ordering) {
self.acsc_with_period_round_ref(2, rm)
}
#[inline]
pub fn acsc_pi(self) -> Self {
self.acsc_with_period(2)
}
#[inline]
pub fn acsc_pi_ref(&self) -> Self {
self.acsc_with_period_ref(2)
}
#[inline]
pub fn acsc_pi_prec_round_assign(&mut self, prec: u64, rm: RoundingMode) -> Ordering {
self.acsc_with_period_prec_round_assign(2, prec, rm)
}
#[inline]
pub fn acsc_pi_prec_assign(&mut self, prec: u64) -> Ordering {
self.acsc_with_period_prec_assign(2, prec)
}
#[inline]
pub fn acsc_pi_round_assign(&mut self, rm: RoundingMode) -> Ordering {
self.acsc_with_period_round_assign(2, rm)
}
#[inline]
pub fn acsc_pi_assign(&mut self) {
self.acsc_with_period_assign(2);
}
#[inline]
pub fn acsc_pi_rational_prec_round(
x: Rational,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
Self::acsc_with_period_rational_prec_round(x, 2, prec, rm)
}
#[inline]
pub fn acsc_pi_rational_prec_round_ref(
x: &Rational,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
Self::acsc_with_period_rational_prec_round_ref(x, 2, prec, rm)
}
#[inline]
pub fn acsc_pi_rational_prec(x: Rational, prec: u64) -> (Self, Ordering) {
Self::acsc_with_period_rational_prec(x, 2, prec)
}
#[inline]
pub fn acsc_pi_rational_prec_ref(x: &Rational, prec: u64) -> (Self, Ordering) {
Self::acsc_with_period_rational_prec_ref(x, 2, prec)
}
}
impl Acsc for Float {
type Output = Self;
#[inline]
fn acsc(self) -> Self {
let prec = self.significant_bits();
self.acsc_prec(prec).0
}
}
impl Acsc for &Float {
type Output = Float;
#[inline]
fn acsc(self) -> Float {
self.acsc_prec_ref(self.significant_bits()).0
}
}
impl AcscAssign for Float {
#[inline]
fn acsc_assign(&mut self) {
let prec = self.significant_bits();
self.acsc_prec_assign(prec);
}
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_acsc<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::acsc_prec, x)
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_acsc_rational<T: PrimitiveFloat>(x: &Rational) -> T
where
Float: PartialOrd<T>,
for<'a> T: ExactFrom<&'a Float>,
{
emulate_rational_to_float_fn(Float::acsc_rational_prec_ref, x)
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_acsc_with_period<T: PrimitiveFloat>(x: T, u: u64) -> T
where
Float: From<T> + PartialOrd<T>,
for<'a> T: ExactFrom<&'a Float> + RoundingFrom<&'a Float>,
{
emulate_float_to_float_fn(|x, prec| Float::acsc_with_period_prec(x, u, prec), x)
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_acsc_with_period_rational<T: PrimitiveFloat>(x: &Rational, u: u64) -> T
where
Float: PartialOrd<T>,
for<'a> T: ExactFrom<&'a Float>,
{
emulate_rational_to_float_fn(
|x, prec| Float::acsc_with_period_rational_prec_ref(x, u, prec),
x,
)
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_acsc_pi<T: PrimitiveFloat>(x: T) -> T
where
Float: From<T> + PartialOrd<T>,
for<'a> T: ExactFrom<&'a Float> + RoundingFrom<&'a Float>,
{
primitive_float_acsc_with_period(x, 2)
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_acsc_pi_rational<T: PrimitiveFloat>(x: &Rational) -> T
where
Float: PartialOrd<T>,
for<'a> T: ExactFrom<&'a Float>,
{
primitive_float_acsc_with_period_rational(x, 2)
}