use crate::InnerFloat::{Finite, Infinity, NaN, Zero};
use crate::float::arithmetic::exp::{get_z_2exp, one_neighbor};
use crate::float::arithmetic::round_near_x::float_round_near_x;
use crate::float::arithmetic::sin_cos::{SINCOS_THRESHOLD, sin_cos_fast};
use crate::{ComparableFloatRef, Float, emulate_float_to_float_fn, emulate_rational_to_float_fn};
use core::cmp::Ordering::{self, Equal, Greater, Less};
use core::cmp::{max, min};
use malachite_base::fail_on_untested_path;
use malachite_base::num::arithmetic::traits::{
Abs, CeilingLogBase2, Cos, CosAssign, DivRoundAssign, FloorLogBase2, FloorSqrt, Mod,
ModPowerOf2, NegAssign, Parity, PowerOf2, Square, SubMul, UnsignedAbs,
};
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::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::integer::Integer;
use malachite_nz::natural::Natural;
use malachite_nz::natural::arithmetic::float::round::float_can_round;
use malachite_nz::platform::Limb;
use malachite_q::Rational;
const MAXI: u64 = 1 << (u64::WIDTH >> 1);
fn cos2_aux(r: &Float, p: u64) -> (Float, u64) {
let exp_r = i64::from(r.get_exponent().unwrap());
assert!(exp_r <= -1);
let (mut x, mut ex) = get_z_2exp(r.clone()); let l = x.trailing_zeros().unwrap();
ex += i64::exact_from(l);
x >>= l;
let mut imax = p / u64::exact_from(-exp_r);
imax += u64::from(imax == 0);
let q = (imax.ceiling_log_base_2() << 1) + 4; let mut s = Integer::power_of_2(p + q); let mut t = s.clone(); let mut i: u64 = 1;
loop {
let m = t.significant_bits();
if m < q {
break;
}
let mut l = x.significant_bits();
if l > m {
l -= m;
x >>= l;
ex += i64::exact_from(l);
}
t *= &x;
t >>= u64::exact_from(-ex);
if i < MAXI {
t.div_round_assign(Integer::from(i * (i + 1)), Floor);
} else {
t.div_round_assign(Integer::from(i), Floor);
t.div_round_assign(Integer::from(i + 1), Floor);
}
if i % 4 == 1 {
s -= &t;
} else {
s += &t;
}
i += 2;
}
let f = Float::from_integer_prec(s, p).0 >> (p + q);
let l = (i - 1) >> 1; (f, ((l + 1).ceiling_log_base_2() << 1) + 1) }
pub(crate) fn sin_bound(t: &Rational, w: u64, upper: bool) -> Rational {
if *t == 0u32 {
return Rational::ZERO;
}
let log = t.floor_log_base_2_abs();
assert!(log < 0);
let mut k = 1u64;
let mut log_factorial = 2u64; 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.floor_log_base_2() + (two_k + 1).floor_log_base_2();
}
let mut s = t.clone();
if k > 1 {
let t_squared = t.square();
let mut term = t.clone();
for j in 1..k {
term *= &t_squared;
term /= Rational::from((j << 1) * ((j << 1) + 1));
term.neg_assign();
s += &term;
}
}
if upper != ((*t > 0u32) == k.odd()) {
let shift = w + 3;
let mut factor = Natural::power_of_2(shift);
if upper == (s > 0u32) {
factor += Natural::ONE;
} else {
factor -= Natural::ONE;
}
s *= Rational::from(factor);
s >>= shift;
}
s
}
pub(crate) fn round_bracket(
lo: &Rational,
hi: &Rational,
prec: u64,
rm: RoundingMode,
) -> Option<(Float, Ordering)> {
let (mut f_lo, mut o_lo) = Float::from_rational_prec_round_ref(lo, prec, rm);
let (mut f_hi, mut o_hi) = Float::from_rational_prec_round_ref(hi, prec, rm);
if o_lo == Equal {
if f_lo == 0u32 {
return None;
}
let up = match rm {
Ceiling => true,
Up => f_lo > 0u32,
Down => f_lo < 0u32,
_ => false,
};
o_lo = if up {
f_lo.increment();
Greater
} else {
Less
};
}
if o_hi == Equal {
if f_hi == 0u32 {
return None;
}
let down = match rm {
Floor => true,
Down => f_hi > 0u32,
Up => f_hi < 0u32,
_ => false,
};
o_hi = if down {
f_hi.decrement();
Less
} else {
Greater
};
}
(o_lo == o_hi && ComparableFloatRef(&f_lo) == ComparableFloatRef(&f_hi)).then_some((f_lo, o_lo))
}
pub(crate) fn round_scaled_bracket(
lo: &Float,
hi: &Float,
k: i64,
prec: u64,
rm: RoundingMode,
) -> Option<(Float, Ordering)> {
let (mut f_lo, mut o_lo) = lo.shl_prec_round_ref(k, prec, rm);
let (mut f_hi, mut o_hi) = hi.shl_prec_round_ref(k, prec, rm);
if o_lo == Equal {
let up = match rm {
Ceiling => true,
Up => f_lo > 0u32,
Down => f_lo < 0u32,
_ => false,
};
o_lo = if up {
f_lo.increment();
Greater
} else {
Less
};
}
if o_hi == Equal {
fail_on_untested_path("round_scaled_bracket, exact upper end");
let down = match rm {
Floor => true,
Down => f_hi > 0u32,
Up => f_hi < 0u32,
_ => false,
};
o_hi = if down {
f_hi.decrement();
Less
} else {
Greater
};
}
(o_lo == o_hi && ComparableFloatRef(&f_lo) == ComparableFloatRef(&f_hi)).then_some((f_lo, o_lo))
}
pub(crate) fn cos_rational_tiny(prec: u64, rm: RoundingMode) -> (Float, Ordering) {
match rm {
Floor | Down => (one_neighbor(prec, false), Less),
_ => (Float::one_prec(prec), Greater),
}
}
fn cos_rational_series(x: &Rational, prec: u64, rm: RoundingMode) -> (Float, Ordering) {
fail_on_untested_path("cos_rational_series");
let x_squared = x.square();
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));
term.neg_assign();
let s_next = &s + &term;
let (lo, hi) = if s < s_next {
(&s, &s_next)
} else {
(&s_next, &s)
};
if let Some(result) = round_bracket(lo, hi, prec, rm) {
return result;
}
s = s_next;
k += 1;
}
}
pub(crate) fn reduce_huge(x: &Rational, exp_x: i64, w: u64) -> Rational {
let two_pi = Rational::exact_from(&(Float::pi_prec(u64::exact_from(exp_x) + w).0 << 1u32));
let k = Integer::rounding_from(x / &two_pi, Nearest).0;
x.sub_mul(&two_pi, &Rational::from(k))
}
pub(crate) fn trig_rational_near_zero_bracket(
y: &Rational,
exp_y: i64,
extra: Option<i64>,
mut w: u64,
target: u64,
cos: bool,
) -> (Rational, Rational) {
let mut increment = Limb::WIDTH;
let w_hint = y.denominator_ref().significant_bits() + target + 64;
loop {
let (lo, hi) = trig_rational_near_zero_step(y, exp_y, extra, w, cos);
if (&hi - &lo) << (target + 4) <= (&lo).abs() {
return (lo, hi);
}
w = max(w + increment, min(w_hint, w << 3));
increment = w >> 1;
}
}
fn trig_rational_near_zero_step(
y: &Rational,
exp_y: i64,
extra: Option<i64>,
w: u64,
cos: bool,
) -> (Rational, Rational) {
let pi = Rational::exact_from(&Float::pi_prec(u64::exact_from(max(exp_y, 1)) + w).0);
let (n, negate, multiple) = if cos {
let n = Integer::rounding_from((y / &pi) << 1u32, Nearest).0;
assert!(n.odd());
let negate = (&n).mod_power_of_2(2) == 1u32;
(n, negate, pi >> 1u32)
} else {
let n = Integer::rounding_from(y / &pi, Nearest).0;
let negate = n.odd();
(n, negate, pi)
};
let delta = y.sub_mul(&multiple, &Rational::from(&n));
let mut e = Rational::power_of_2(1 - i64::exact_from(w));
if let Some(extra) = extra {
e += Rational::power_of_2(extra);
}
let d_lo = &delta - &e;
let d_hi = delta + e;
let sin_lo = sin_bound(&d_lo, w, false);
let sin_hi = sin_bound(&d_hi, w, true);
if negate {
(-sin_hi, -sin_lo)
} else {
(sin_lo, sin_hi)
}
}
pub(crate) fn trig_rational_near_zero(
y: &Rational,
exp_y: i64,
prec: u64,
rm: RoundingMode,
extra: Option<i64>,
mut w: u64,
cos: bool,
) -> (Float, Ordering) {
let mut increment = Limb::WIDTH;
let w_hint = y.denominator_ref().significant_bits() + prec + 64;
loop {
let (lo, hi) = trig_rational_near_zero_step(y, exp_y, extra, w, cos);
if let Some(result) = round_bracket(&lo, &hi, prec, rm) {
return result;
}
w = max(w + increment, min(w_hint, w << 3));
increment = w >> 1;
}
}
pub(crate) fn cos_rational_helper(x: &Rational, prec: u64, rm: RoundingMode) -> (Float, Ordering) {
assert_ne!(rm, Exact, "Inexact cos");
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 <= Float::MIN_EXPONENT_I64 {
return cos_rational_series(x, prec, rm);
}
let huge = exp_x >= Float::MAX_EXPONENT_I64;
let mut w = prec + 10;
let mut increment = Limb::WIDTH;
loop {
let reduced;
let (y, extra) = if huge {
reduced = reduce_huge(x, exp_x, w);
(&reduced, Some(2 - i64::exact_from(w)))
} else {
(x, None)
};
let (y_f, y_o) = Float::from_rational_prec_ref(y, w);
if !huge && y_o == Equal {
return cos_prec_round_normal_ref(&y_f, prec, rm);
}
let c_f = (&y_f).cos();
let exp_y = y.floor_log_base_2_abs() + 1;
let exp_c = c_f
.get_exponent()
.map_or(Float::MIN_EXPONENT_I64, i64::from);
if exp_c < 0 {
let cancel = u64::exact_from(-exp_c);
if cancel >= max(NEAR_ZERO_MIN_CANCEL, prec >> 4) {
return trig_rational_near_zero(y, exp_y, prec, rm, extra, w, true);
}
}
let w_i = i64::exact_from(w);
let mut delta = Rational::power_of_2(exp_c - w_i) + Rational::power_of_2(exp_y - w_i);
if let Some(extra) = extra {
delta += Rational::power_of_2(extra);
}
let c = Rational::exact_from(&c_f);
if let Some(result) = round_bracket(&(&c - &delta), &(c + delta), prec, rm) {
return result;
}
w += increment;
increment = w >> 1;
}
}
pub(crate) const NEAR_ZERO_MIN_CANCEL: u64 = 64;
fn trig_near_zero_approx(
x: &Float,
w: u64,
p: &mut u64,
p_hint: u64,
cos: bool,
) -> (Integer, u64, i64) {
let e = u64::exact_from(x.get_exponent().unwrap());
let mut x_low = Float::from_float_prec_ref(x, e + 16).0;
if cos {
x_low <<= 1u32;
}
let q = x_low.div_prec(Float::pi_prec(e + 16).0, e + 16).0;
let n = Integer::rounding_from(q, Nearest).0;
let negate = if cos {
assert!(n.odd());
(&n).mod_power_of_2(2) == 1u32
} else {
assert_ne!(n, 0u32);
n.odd()
};
let x_sig = x.significand_ref().unwrap();
let x_bits = x_sig.significant_bits();
let x_exp = i64::from(x.get_exponent().unwrap()) - i64::exact_from(x_bits);
loop {
let (pi_sig, mut pi_exp) = get_z_2exp(Float::pi_prec(e + *p).0);
if cos {
pi_exp -= 1; }
let d_exp = min(x_exp, pi_exp);
let a = Integer::from_sign_and_abs_ref(x > &0u32, x_sig) << u64::exact_from(x_exp - d_exp);
let b = (&n * pi_sig) << u64::exact_from(pi_exp - d_exp);
let d = a - b;
let d_neg = d < 0u32;
let mut d_abs = d.unsigned_abs();
let d_bits = d_abs.significant_bits();
let delta_exp = d_exp + i64::exact_from(d_bits);
if delta_exp + i64::exact_from(*p) <= i64::exact_from(w) {
let needed = u64::exact_from(i64::exact_from(w) - delta_exp + 2);
*p = max(
needed,
min(max(*p << 1, min(p_hint, *p << 3)), max(p_hint, needed)),
);
continue;
}
let mut d_exp = d_exp;
if d_bits > w {
let shift = d_bits - w;
d_abs >>= shift;
d_exp += i64::exact_from(shift);
}
let q = Integer::from(
(&d_abs).square() >> u64::exact_from(-((d_exp << 1) + i64::exact_from(w))),
);
let mut r = Integer::power_of_2(w);
let mut term = Integer::power_of_2(w);
let mut k = 1u64;
let mut terms = 0u64;
loop {
term *= &q;
term >>= w;
term.div_round_assign(Integer::from((k << 1) * ((k << 1) + 1)), Floor);
term.neg_assign();
if term == 0u32 {
break;
}
r += &term;
k += 1;
terms += 1;
}
let mut m = Integer::from(d_abs) * r;
if d_neg != negate {
m.neg_assign();
}
return (
m,
w + 2 + (terms + 2).ceiling_log_base_2(),
d_exp - i64::exact_from(w),
);
}
}
fn trig_near_zero_pi_precs(x: &Float, w: u64, cancel: u64) -> (u64, u64) {
let e = u64::exact_from(x.get_exponent().unwrap());
let x_bits = x.significand_ref().unwrap().significant_bits();
(w + cancel + 2, (x_bits + w + 2).saturating_sub(e))
}
pub(crate) fn trig_near_zero(
x: &Float,
prec: u64,
rm: RoundingMode,
cancel: u64,
cos: bool,
) -> (Float, Ordering) {
let w = prec + 64;
let (mut p, p_hint) = trig_near_zero_pi_precs(x, w, cancel);
loop {
let (m, abs_err, shift) = trig_near_zero_approx(x, w, &mut p, p_hint, cos);
let m_bits = m.significant_bits();
assert!(m_bits <= const { Float::MAX_EXPONENT as u64 });
let err = m_bits - abs_err;
let s = Float::from_integer_prec(m, m_bits).0;
if float_can_round(s.significand_ref().unwrap(), err, prec, rm) {
return s.shl_prec_round(shift, prec, rm);
}
p += max(p >> 2, Limb::WIDTH);
}
}
pub(crate) fn trig_near_zero_bracket(
x: &Float,
w: u64,
cancel: u64,
cos: bool,
) -> (Rational, Rational) {
let (mut p, p_hint) = trig_near_zero_pi_precs(x, w, cancel);
let (m, abs_err, shift) = trig_near_zero_approx(x, w, &mut p, p_hint, cos);
let e = Integer::power_of_2(abs_err);
(
Rational::from(&m - &e) << shift,
Rational::from(m + e) << shift,
)
}
pub(crate) enum TrigStep {
Retry,
Done(Float),
NearZero(u64),
}
fn cos_ziv_step(
x: &Float,
exp_x: i64,
prec: u64,
rm: RoundingMode,
reduce: bool,
k0: u64,
m: &mut u64,
cancel: &mut i64,
) -> TrigStep {
let mut r = if reduce {
let c = Float::pi_prec(u64::exact_from(exp_x) + *m - 1).0 << 1u32; let xr = x.ieee_remainder_prec_ref_val(c, *m).0;
if xr == 0u32 {
return TrigStep::Retry;
}
xr.square_round(Ceiling).0 } else {
x.square_prec_round_ref(*m, Ceiling).0 };
let exp_r = i64::from(r.get_exponent().unwrap());
let k = k0 + 1 + (u64::exact_from(max(0, exp_r)) >> 1);
r >>= k << 1; let (mut s, err_ulps) = cos2_aux(&r, *m);
let one = Float::one_prec(*m);
for _ in 0..k {
s.square_prec_round_assign(*m, Ceiling); s <<= 1u32; s.sub_prec_assign_ref(&one, *m); if s == 0u32 {
fail_on_untested_path("cos_ziv_step, s == 0 after doubling");
return TrigStep::Retry;
}
assert!(s.get_exponent().unwrap() <= 1);
}
let mut err_ulps = (err_ulps << 1) + 1;
if reduce {
err_ulps += 1;
}
let err_bits = err_ulps.ceiling_log_base_2() + (k << 1);
let exp_s = i64::from(s.get_exponent().unwrap());
let err = exp_s + i64::exact_from(*m) - i64::exact_from(err_bits);
if err > 0 && float_can_round(s.significand_ref().unwrap(), u64::exact_from(err), prec, rm) {
return TrigStep::Done(s);
}
if exp_s == 1 && *m > err_bits && *m - err_bits >= prec + u64::from(rm == Nearest) {
let neighbor = one_neighbor(*m, false);
return TrigStep::Done(if s < 0u32 { -neighbor } else { neighbor });
}
let bound = max(exp_s, i64::exact_from(err_bits) - i64::exact_from(*m)) + 1;
if bound < 0 {
let c = u64::exact_from(-bound);
if c >= max(NEAR_ZERO_MIN_CANCEL, prec >> 4) {
return TrigStep::NearZero(c);
}
}
if exp_s < *cancel {
*m += u64::exact_from(*cancel - exp_s);
*cancel = exp_s;
}
TrigStep::Retry
}
fn cos_prec_round_normal_ref(x: &Float, prec: u64, rm: RoundingMode) -> (Float, Ordering) {
assert_ne!(rm, Exact, "Inexact cos");
let exp_x = i64::from(x.get_exponent().unwrap());
let neg_err = -(exp_x << 1);
if neg_err > 0 {
let err = u64::exact_from(neg_err) + 1;
if err > prec + 1 {
return float_round_near_x(&Float::ONE, min(err, prec + 2), false, prec, rm).unwrap();
}
}
if prec >= SINCOS_THRESHOLD {
return sin_cos_fast(x, prec, rm, false, true).1.unwrap();
}
cos_basic(x, exp_x, prec, rm)
}
pub(crate) fn cos_basic(x: &Float, exp_x: i64, prec: u64, rm: RoundingMode) -> (Float, Ordering) {
let k0 = (prec / 3).floor_sqrt();
let mut m = prec + (prec.ceiling_log_base_2() << 1) + (k0 << 1) + 4;
let reduce = exp_x >= 3;
let mut cancel: i64 = 0;
let mut increment = Limb::WIDTH;
let s = loop {
match cos_ziv_step(x, exp_x, prec, rm, reduce, k0, &mut m, &mut cancel) {
TrigStep::Done(s) => break s,
TrigStep::NearZero(c) => return trig_near_zero(x, prec, rm, c, true),
TrigStep::Retry => {}
}
m += increment;
increment = m >> 1;
};
Float::from_float_prec_round(s, prec, rm)
}
impl Float {
#[inline]
pub fn cos_prec_round(self, prec: u64, rm: RoundingMode) -> (Self, Ordering) {
self.cos_prec_round_ref(prec, rm)
}
pub fn cos_prec_round_ref(&self, prec: u64, rm: RoundingMode) -> (Self, Ordering) {
assert_ne!(prec, 0);
match &self.0 {
NaN | Infinity { .. } => (Self::NAN, Equal),
Zero { .. } => (Self::one_prec(prec), Equal),
Finite { .. } => cos_prec_round_normal_ref(self, prec, rm),
}
}
#[inline]
pub fn cos_prec(self, prec: u64) -> (Self, Ordering) {
self.cos_prec_round(prec, Nearest)
}
#[inline]
pub fn cos_prec_ref(&self, prec: u64) -> (Self, Ordering) {
self.cos_prec_round_ref(prec, Nearest)
}
#[inline]
pub fn cos_round(self, rm: RoundingMode) -> (Self, Ordering) {
let prec = self.significant_bits();
self.cos_prec_round(prec, rm)
}
#[inline]
pub fn cos_round_ref(&self, rm: RoundingMode) -> (Self, Ordering) {
self.cos_prec_round_ref(self.significant_bits(), rm)
}
#[inline]
pub fn cos_prec_round_assign(&mut self, prec: u64, rm: RoundingMode) -> Ordering {
let o;
(*self, o) = self.cos_prec_round_ref(prec, rm);
o
}
#[inline]
pub fn cos_prec_assign(&mut self, prec: u64) -> Ordering {
self.cos_prec_round_assign(prec, Nearest)
}
#[inline]
pub fn cos_round_assign(&mut self, rm: RoundingMode) -> Ordering {
let prec = self.significant_bits();
self.cos_prec_round_assign(prec, rm)
}
}
impl Float {
#[inline]
#[allow(clippy::needless_pass_by_value)]
pub fn cos_rational_prec_round(x: Rational, prec: u64, rm: RoundingMode) -> (Self, Ordering) {
Self::cos_rational_prec_round_ref(&x, prec, rm)
}
pub fn cos_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);
}
cos_rational_helper(x, prec, rm)
}
#[inline]
#[allow(clippy::needless_pass_by_value)]
pub fn cos_rational_prec(x: Rational, prec: u64) -> (Self, Ordering) {
Self::cos_rational_prec_round_ref(&x, prec, Nearest)
}
#[inline]
pub fn cos_rational_prec_ref(x: &Rational, prec: u64) -> (Self, Ordering) {
Self::cos_rational_prec_round_ref(x, prec, Nearest)
}
}
pub(crate) fn half_constant<F: Fn(u64, RoundingMode) -> (Float, Ordering)>(
constant: F,
negative: bool,
prec: u64,
rm: RoundingMode,
) -> (Float, Ordering) {
let (c, o) = signed_constant(constant, negative, prec, rm);
(c >> 1u32, o)
}
pub(crate) fn signed_constant<F: Fn(u64, RoundingMode) -> (Float, Ordering)>(
constant: F,
negative: bool,
prec: u64,
rm: RoundingMode,
) -> (Float, Ordering) {
if negative {
let (c, o) = constant(prec, -rm);
(-c, o.reverse())
} else {
constant(prec, rm)
}
}
pub(crate) fn phi_minus_1_prec_round(prec: u64, rm: RoundingMode) -> (Float, Ordering) {
let (phi, o) = Float::phi_prec_round(prec + 1, rm);
let (r, o_sub) = phi.sub_prec_round(Float::ONE, prec, Exact);
assert_eq!(o_sub, Equal);
(r, o)
}
pub(crate) fn cos_turns_special_case(
q: &Rational,
prec: u64,
rm: RoundingMode,
) -> Option<(Float, Ordering)> {
let d = q.denominator_ref();
if *d > 12u32 {
return None;
}
let d = u64::exact_from(d);
let n = u64::exact_from(&Integer::from(q.numerator_ref()).mod_op(Integer::from(d)));
match d {
1 => Some((Float::one_prec(prec), Equal)),
2 => Some((-Float::one_prec(prec), Equal)),
4 => Some((Float::ZERO, Equal)),
6 => Some((Float::one_prec(prec) >> 1u32, Equal)),
3 => Some((-(Float::one_prec(prec) >> 1u32), Equal)),
_ if rm == Exact => None,
8 => Some(half_constant(
Float::sqrt_2_prec_round,
n == 3 || n == 5,
prec,
rm,
)),
12 => Some(half_constant(
|prec, rm| const { Float::const_from_unsigned(3) }.sqrt_prec_round(prec, rm),
n == 5 || n == 7,
prec,
rm,
)),
5 => Some(if n == 1 || n == 4 {
half_constant(phi_minus_1_prec_round, false, prec, rm)
} else {
half_constant(Float::phi_prec_round, true, prec, rm)
}),
10 => Some(if n == 1 || n == 9 {
half_constant(Float::phi_prec_round, false, prec, rm)
} else {
half_constant(phi_minus_1_prec_round, true, prec, rm)
}),
_ => None,
}
}
fn trig_turns_near_zero_reduce(q: &Rational, cos: bool) -> Option<(bool, Rational)> {
Some(if cos {
let m = Integer::rounding_from(q << 2u32, Nearest).0;
if m.even() {
fail_on_untested_path("trig_turns_near_zero, not near an odd multiple of 1/4");
return None;
}
(
(&m).mod_power_of_2(2) == 1u32,
q - (Rational::from(m) >> 2u32),
)
} else {
let m = Integer::rounding_from(q << 1u32, Nearest).0;
(m.odd(), q - (Rational::from(m) >> 1u32))
})
}
fn trig_turns_near_zero_step(negate: bool, d: &Rational, w: u64) -> Option<(Rational, Rational)> {
let pi_lo = Rational::exact_from(&Float::pi_prec_round(w, Floor).0);
let pi_hi = &pi_lo + Rational::power_of_2(2 - i64::exact_from(w));
let two_d = d << 1u32;
let (t_lo, t_hi) = if two_d >= 0u32 {
(&two_d * pi_lo, two_d * pi_hi)
} else {
(&two_d * pi_hi, two_d * pi_lo)
};
if t_hi.ge_abs(&1u32) || t_lo.ge_abs(&1u32) {
fail_on_untested_path("trig_turns_near_zero, distance not small");
return None;
}
let sin_lo = sin_bound(&t_lo, w, false);
let sin_hi = sin_bound(&t_hi, w, true);
Some(if negate {
(-sin_hi, -sin_lo)
} else {
(sin_lo, sin_hi)
})
}
pub(crate) fn trig_turns_near_zero(
q: &Rational,
prec: u64,
rm: RoundingMode,
cos: bool,
) -> Option<(Float, Ordering)> {
let (negate, d) = trig_turns_near_zero_reduce(q, cos)?;
let mut w = prec + 64;
loop {
let (lo, hi) = trig_turns_near_zero_step(negate, &d, w)?;
if let Some(result) = round_bracket(&lo, &hi, prec, rm) {
return Some(result);
}
w <<= 1;
}
}
pub(crate) fn trig_turns_near_zero_bracket(
q: &Rational,
target: u64,
cos: bool,
) -> Option<(Rational, Rational)> {
let (negate, d) = trig_turns_near_zero_reduce(q, cos)?;
let mut w = target + 64;
loop {
let (lo, hi) = trig_turns_near_zero_step(negate, &d, w)?;
if (&hi - &lo) << (target + 4) <= (&lo).abs() {
return Some((lo, hi));
}
w <<= 1;
}
}
pub(crate) fn cos_with_period_prec_round_normal_ref(
x: &Float,
u: u64,
prec: u64,
rm: RoundingMode,
) -> (Float, Ordering) {
let xr;
let xp = if x.lt_abs(&u) {
x
} else {
let p = i64::exact_from(x.get_prec().unwrap()) - i64::from(x.get_exponent().unwrap());
let (r, o) =
x.rem_unsigned_prec_round_ref(u, u64::WIDTH + u64::exact_from(max(p, 0)), Exact);
assert_eq!(o, Equal);
if r == 0u32 {
return (Float::one_prec(prec), Equal);
}
xr = r;
&xr
};
let xf;
let xp = if (xp << 1u32).gt_abs(&u) {
let p = i64::exact_from(xp.get_prec().unwrap()) - i64::from(xp.get_exponent().unwrap());
let step = if *xp > 0u32 {
-Float::from(u)
} else {
Float::from(u)
};
let (f, o) = xp.add_prec_ref_val(step, u64::WIDTH + u64::exact_from(max(p, 0)));
if o != Equal {
assert_ne!(rm, Exact, "Inexact cos_with_period");
return float_round_near_x(&Float::ONE, prec + 2, false, prec, rm).unwrap();
}
xf = f;
&xf
} else {
xp
};
let exp_x = i64::from(xp.get_exponent().unwrap());
let log2u = if u == 1 {
0
} else {
i64::exact_from(u.ceiling_log_base_2()) - 1
};
let erra = -(exp_x << 1);
let errb = 5 - (log2u << 1);
if erra > errb {
let err = u64::exact_from(erra - errb);
if err > prec + 1 {
assert_ne!(rm, Exact, "Inexact cos_with_period");
return float_round_near_x(&Float::ONE, min(err, prec + 2), false, prec, rm).unwrap();
}
}
if exp_x >= i64::exact_from(u.significant_bits()) - 4
&& let Some(result) =
cos_turns_special_case(&(Rational::exact_from(xp) / Rational::from(u)), prec, rm)
{
return result;
}
assert_ne!(rm, Exact, "Inexact cos_with_period");
let mut prec_t =
prec + u64::exact_from(max(exp_x, i64::exact_from(prec.ceiling_log_base_2()))) + 8;
let mut increment = Limb::WIDTH;
let u_float = Float::from(u);
loop {
let mut t = Float::pi_prec(prec_t).0 << 1u32;
t.mul_prec_assign_ref(xp, prec_t);
t.div_prec_assign_ref(&u_float, prec_t);
if t == 0u32 {
fail_on_untested_path(
"cos_with_period_prec_round_normal_ref, division by u underflowed",
);
return match rm {
Floor | Down => (one_neighbor(prec, false), Less),
_ => (Float::one_prec(prec), Greater),
};
}
let exp_t = i64::from(t.get_exponent().unwrap());
let prec_t_i = i64::exact_from(prec_t);
let mut err = exp_t + 2 - prec_t_i;
t.cos_prec_assign(prec_t);
let exp_t = t.get_exponent().map_or(Float::MIN_EXPONENT_I64, i64::from);
if exp_t < 0 {
let cancel = u64::exact_from(-exp_t);
if cancel >= max(NEAR_ZERO_MIN_CANCEL, prec >> 4)
&& let Some(result) = trig_turns_near_zero(
&(Rational::exact_from(xp) / Rational::from(u)),
prec,
rm,
true,
)
{
return result;
}
}
err = if err < exp_t - prec_t_i {
exp_t - prec_t_i
} else {
err + 1
};
err = exp_t - err;
if err > 0 && float_can_round(t.significand_ref().unwrap(), u64::exact_from(err), prec, rm)
{
return Float::from_float_prec_round(t, prec, rm);
}
prec_t += increment;
increment = prec_t >> 1;
}
}
impl Float {
#[inline]
pub fn cos_with_period_prec_round(
self,
u: u64,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
self.cos_with_period_prec_round_ref(u, prec, rm)
}
pub fn cos_with_period_prec_round_ref(
&self,
u: u64,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
assert_ne!(prec, 0);
match &self.0 {
_ if u == 0 => (Self::NAN, Equal),
NaN | Infinity { .. } => (Self::NAN, Equal),
Zero { .. } => (Self::one_prec(prec), Equal),
Finite { .. } => cos_with_period_prec_round_normal_ref(self, u, prec, rm),
}
}
#[inline]
pub fn cos_with_period_prec(self, u: u64, prec: u64) -> (Self, Ordering) {
self.cos_with_period_prec_round(u, prec, Nearest)
}
#[inline]
pub fn cos_with_period_prec_ref(&self, u: u64, prec: u64) -> (Self, Ordering) {
self.cos_with_period_prec_round_ref(u, prec, Nearest)
}
#[inline]
pub fn cos_with_period_round(self, u: u64, rm: RoundingMode) -> (Self, Ordering) {
let prec = self.significant_bits();
self.cos_with_period_prec_round(u, prec, rm)
}
#[inline]
pub fn cos_with_period_round_ref(&self, u: u64, rm: RoundingMode) -> (Self, Ordering) {
self.cos_with_period_prec_round_ref(u, self.significant_bits(), rm)
}
#[inline]
pub fn cos_with_period(self, u: u64) -> Self {
let prec = self.significant_bits();
self.cos_with_period_prec(u, prec).0
}
#[inline]
pub fn cos_with_period_ref(&self, u: u64) -> Self {
self.cos_with_period_prec_ref(u, self.significant_bits()).0
}
#[inline]
pub fn cos_with_period_prec_round_assign(
&mut self,
u: u64,
prec: u64,
rm: RoundingMode,
) -> Ordering {
let o;
(*self, o) = self.cos_with_period_prec_round_ref(u, prec, rm);
o
}
#[inline]
pub fn cos_with_period_prec_assign(&mut self, u: u64, prec: u64) -> Ordering {
self.cos_with_period_prec_round_assign(u, prec, Nearest)
}
#[inline]
pub fn cos_with_period_round_assign(&mut self, u: u64, rm: RoundingMode) -> Ordering {
let prec = self.significant_bits();
self.cos_with_period_prec_round_assign(u, prec, rm)
}
#[inline]
pub fn cos_with_period_assign(&mut self, u: u64) {
let prec = self.significant_bits();
self.cos_with_period_prec_assign(u, prec);
}
}
pub(crate) fn cos_turns_helper(q: &Rational, prec: u64, rm: RoundingMode) -> (Float, Ordering) {
let exp_q = q.floor_log_base_2_abs() + 1;
let err = -(exp_q << 1) - 5;
if err > 0 {
let err = u64::exact_from(err);
if err > prec + 1 {
assert_ne!(rm, Exact, "Inexact cos_with_period");
return float_round_near_x(&Float::ONE, min(err, prec + 2), false, prec, rm).unwrap();
}
}
if exp_q >= -4
&& let Some(result) = cos_turns_special_case(q, prec, rm)
{
return result;
}
assert_ne!(rm, Exact, "Inexact cos_with_period");
let mut w = prec + prec.ceiling_log_base_2() + 8;
let mut increment = Limb::WIDTH;
loop {
let mut t = Float::pi_prec(w).0 << 1u32;
t.mul_prec_assign(Float::from_rational_prec_ref(q, w).0, w);
if t == 0u32 {
fail_on_untested_path("cos_turns_helper, 2 pi q underflowed");
return match rm {
Floor | Down => (one_neighbor(prec, false), Less),
_ => (Float::one_prec(prec), Greater),
};
}
let exp_t = i64::from(t.get_exponent().unwrap());
let w_i = i64::exact_from(w);
let mut err = exp_t + 2 - w_i;
t.cos_prec_assign(w);
let exp_t = t.get_exponent().map_or(Float::MIN_EXPONENT_I64, i64::from);
if exp_t < 0 {
let cancel = u64::exact_from(-exp_t);
if cancel >= max(NEAR_ZERO_MIN_CANCEL, prec >> 4)
&& let Some(result) = trig_turns_near_zero(q, prec, rm, true)
{
return result;
}
}
err = if err < exp_t - w_i {
exp_t - w_i
} else {
err + 1
};
err = exp_t - err;
if err > 0 && float_can_round(t.significand_ref().unwrap(), u64::exact_from(err), prec, rm)
{
return Float::from_float_prec_round(t, prec, rm);
}
w += increment;
increment = w >> 1;
}
}
impl Float {
#[inline]
#[allow(clippy::needless_pass_by_value)]
pub fn cos_with_period_rational_prec_round(
x: Rational,
u: u64,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
Self::cos_with_period_rational_prec_round_ref(&x, u, prec, rm)
}
pub fn cos_with_period_rational_prec_round_ref(
x: &Rational,
u: u64,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
assert_ne!(prec, 0);
if u == 0 {
return (Self::NAN, Equal);
}
if *x == 0u32 {
return (Self::one_prec(prec), Equal);
}
let q = x / Rational::from(u);
let whole = Rational::from(Integer::rounding_from(&q, Nearest).0);
let q = q - whole;
if q == 0u32 {
return (Self::one_prec(prec), Equal);
}
cos_turns_helper(&q, prec, rm)
}
#[inline]
#[allow(clippy::needless_pass_by_value)]
pub fn cos_with_period_rational_prec(x: Rational, u: u64, prec: u64) -> (Self, Ordering) {
Self::cos_with_period_rational_prec_round_ref(&x, u, prec, Nearest)
}
#[inline]
pub fn cos_with_period_rational_prec_ref(x: &Rational, u: u64, prec: u64) -> (Self, Ordering) {
Self::cos_with_period_rational_prec_round_ref(x, u, prec, Nearest)
}
}
impl Float {
#[inline]
pub fn cos_pi_prec_round(self, prec: u64, rm: RoundingMode) -> (Self, Ordering) {
self.cos_with_period_prec_round(2, prec, rm)
}
#[inline]
pub fn cos_pi_prec_round_ref(&self, prec: u64, rm: RoundingMode) -> (Self, Ordering) {
self.cos_with_period_prec_round_ref(2, prec, rm)
}
#[inline]
pub fn cos_pi_prec(self, prec: u64) -> (Self, Ordering) {
self.cos_with_period_prec(2, prec)
}
#[inline]
pub fn cos_pi_prec_ref(&self, prec: u64) -> (Self, Ordering) {
self.cos_with_period_prec_ref(2, prec)
}
#[inline]
pub fn cos_pi_round(self, rm: RoundingMode) -> (Self, Ordering) {
self.cos_with_period_round(2, rm)
}
#[inline]
pub fn cos_pi_round_ref(&self, rm: RoundingMode) -> (Self, Ordering) {
self.cos_with_period_round_ref(2, rm)
}
#[inline]
pub fn cos_pi(self) -> Self {
let prec = self.significant_bits();
self.cos_pi_prec(prec).0
}
#[inline]
pub fn cos_pi_ref(&self) -> Self {
self.cos_pi_prec_ref(self.significant_bits()).0
}
#[inline]
pub fn cos_pi_prec_round_assign(&mut self, prec: u64, rm: RoundingMode) -> Ordering {
self.cos_with_period_prec_round_assign(2, prec, rm)
}
#[inline]
pub fn cos_pi_prec_assign(&mut self, prec: u64) -> Ordering {
self.cos_with_period_prec_assign(2, prec)
}
#[inline]
pub fn cos_pi_round_assign(&mut self, rm: RoundingMode) -> Ordering {
self.cos_with_period_round_assign(2, rm)
}
#[inline]
pub fn cos_pi_assign(&mut self) {
let prec = self.significant_bits();
self.cos_pi_prec_assign(prec);
}
#[inline]
#[allow(clippy::needless_pass_by_value)]
pub fn cos_pi_rational_prec_round(
x: Rational,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
Self::cos_with_period_rational_prec_round_ref(&x, 2, prec, rm)
}
#[inline]
pub fn cos_pi_rational_prec_round_ref(
x: &Rational,
prec: u64,
rm: RoundingMode,
) -> (Self, Ordering) {
Self::cos_with_period_rational_prec_round_ref(x, 2, prec, rm)
}
#[inline]
#[allow(clippy::needless_pass_by_value)]
pub fn cos_pi_rational_prec(x: Rational, prec: u64) -> (Self, Ordering) {
Self::cos_with_period_rational_prec_ref(&x, 2, prec)
}
#[inline]
pub fn cos_pi_rational_prec_ref(x: &Rational, prec: u64) -> (Self, Ordering) {
Self::cos_with_period_rational_prec_ref(x, 2, prec)
}
}
impl Cos for Float {
type Output = Self;
#[inline]
fn cos(self) -> Self {
let prec = self.significant_bits();
self.cos_prec_round(prec, Nearest).0
}
}
impl Cos for &Float {
type Output = Float;
#[inline]
fn cos(self) -> Float {
self.cos_prec_round_ref(self.significant_bits(), Nearest).0
}
}
impl CosAssign for Float {
#[inline]
fn cos_assign(&mut self) {
let prec = self.significant_bits();
self.cos_prec_round_assign(prec, Nearest);
}
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_cos<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::cos_prec, x)
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_cos_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::cos_rational_prec_ref, x)
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_cos_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::cos_with_period_prec(x, u, prec), x)
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_cos_with_period_rational<T: PrimitiveFloat>(x: &Rational, u: u64) -> T
where
Float: PartialOrd<T>,
for<'a> T: ExactFrom<&'a Float> + RoundingFrom<&'a Float>,
{
emulate_rational_to_float_fn(
|x, prec| Float::cos_with_period_rational_prec_ref(x, u, prec),
x,
)
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_cos_pi<T: PrimitiveFloat>(x: T) -> T
where
Float: From<T> + PartialOrd<T>,
for<'a> T: ExactFrom<&'a Float> + RoundingFrom<&'a Float>,
{
primitive_float_cos_with_period(x, 2)
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_cos_pi_rational<T: PrimitiveFloat>(x: &Rational) -> T
where
Float: PartialOrd<T>,
for<'a> T: ExactFrom<&'a Float> + RoundingFrom<&'a Float>,
{
primitive_float_cos_with_period_rational(x, 2)
}