use crate::InnerFloat::{Finite, Infinity, NaN, Zero};
use crate::float::arithmetic::cos::{
NEAR_ZERO_MIN_CANCEL, cos_rational_helper, cos_rational_tiny, cos_turns_helper,
cos_turns_special_case, cos_with_period_prec_round_normal_ref, reduce_huge, round_bracket,
trig_near_zero, trig_rational_near_zero, trig_turns_near_zero,
};
use crate::float::arithmetic::exp::get_z_2exp;
use crate::float::arithmetic::round_near_x::float_round_near_x;
use crate::float::arithmetic::sin::{
SCALED_INPUT_EXPONENT, sin_rational_helper, sin_turns_helper, sin_turns_special_case,
sin_with_period_prec_round_normal_ref,
};
use crate::{Float, emulate_float_to_float_pair_fn, emulate_rational_to_float_pair_fn};
use alloc::vec;
use core::cmp::Ordering::{self, Equal};
use core::cmp::{max, min};
use core::mem::swap;
use malachite_base::num::arithmetic::traits::{
Abs, CeilingLogBase2, FloorSqrt, IsPowerOf2, NegAssign, Parity, PowerOf2, SinCos, SinCosAssign,
Square, UnsignedAbs,
};
use malachite_base::num::basic::floats::PrimitiveFloat;
use malachite_base::num::basic::integers::PrimitiveInt;
use malachite_base::num::basic::traits::{
NaN as NaNTrait, NegativeZero as NegativeZeroTrait, One, Zero as ZeroTrait,
};
use malachite_base::num::comparison::traits::{EqAbs, 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_base::{fail_on_untested_path, split_into_chunks_mut};
use malachite_nz::integer::Integer;
use malachite_nz::natural::arithmetic::float::round::float_can_round;
use malachite_nz::platform::Limb;
use malachite_q::Rational;
enum SinCosStep {
Retry,
Done(Float, Float),
NearZeroCos { cancel: u64, sin_negative: bool },
NearZeroSin { cancel: u64, cos_negative: bool },
}
fn sin_cos_ziv_step(
x: &Float,
exp_x: i64,
prec: u64,
rm: RoundingMode,
reduce: bool,
m: &mut u64,
) -> SinCosStep {
let near_zero_threshold = max(NEAR_ZERO_MIN_CANCEL, (prec >> 1) + 1);
let m_i = i64::exact_from(*m);
let xr;
let xx = if reduce {
let c_prec = u64::exact_from(exp_x) + *m - 1;
let pi = Float::pi_prec(c_prec).0;
xr = x.ieee_remainder_prec_ref_val(&pi << 1u32, *m).0;
let c = pi.sub_prec_round((&xr).abs(), c_prec, Down).0;
let threshold = 3 - m_i;
let xr_small = xr == 0u32 || i64::from(xr.get_exponent().unwrap()) < threshold;
let c_small = c == 0u32 || i64::from(c.get_exponent().unwrap()) < threshold;
if xr_small || c_small {
let cancel = *m - 4;
return if cancel >= near_zero_threshold {
SinCosStep::NearZeroSin {
cancel,
cos_negative: c_small,
}
} else {
SinCosStep::Retry
};
}
&xr
} else {
x
};
let sign = *xx < 0u32;
let c = xx.cos_prec_round_ref(*m, Down).0;
let exp_c = c.get_exponent().map_or(Float::MIN_EXPONENT_I64, i64::from);
let bound_exp = if reduce { max(exp_c, 2 - m_i) } else { exp_c } + 1;
if bound_exp < 0 && exp_x >= 1 {
let cancel = u64::exact_from(-bound_exp);
if cancel >= near_zero_threshold {
return SinCosStep::NearZeroCos {
cancel,
sin_negative: sign,
};
}
}
let err = if reduce { exp_c + m_i - 3 } else { m_i };
if c == 0u32
|| err <= 0
|| !float_can_round(c.significand_ref().unwrap(), u64::exact_from(err), prec, rm)
{
return SinCosStep::Retry;
}
let mut s = Float::ONE.sub_prec(c.square_round_ref(Ceiling).0, *m).0;
if s == 0u32 {
let cancel = (*m >> 1).saturating_sub(1);
if reduce && cancel >= near_zero_threshold {
return SinCosStep::NearZeroSin {
cancel,
cos_negative: c < 0u32,
};
}
fail_on_untested_path("sin_cos_ziv_step, 1 - c^2 rounded to zero");
*m = max(*m, x.significant_bits()) << 1;
return SinCosStep::Retry;
}
s.sqrt_prec_assign(*m);
let exp_s = i64::from(s.get_exponent().unwrap());
let err = 3 + if reduce { 3 } else { 0 } - exp_s;
if sign {
s.neg_assign();
}
let bound_exp = max(exp_s, err - m_i) + 1;
if reduce && bound_exp < 0 {
let cancel = u64::exact_from(-bound_exp);
if cancel >= near_zero_threshold {
return SinCosStep::NearZeroSin {
cancel,
cos_negative: c < 0u32,
};
}
}
let err = exp_s + m_i - err;
if err > 0 && float_can_round(s.significand_ref().unwrap(), u64::exact_from(err), prec, rm) {
return SinCosStep::Done(s, c);
}
if err < i64::exact_from(prec) {
*m += u64::exact_from(i64::exact_from(prec) - err);
}
if exp_s == 1 && s.eq_abs(&1u32) {
*m <<= 1;
}
SinCosStep::Retry
}
fn near_one(err: u64, negative: bool, prec: u64, rm: RoundingMode) -> (Float, Ordering) {
let err = min(err, prec + 2);
if negative {
let (r, o) = float_round_near_x(&Float::ONE, err, false, prec, -rm).unwrap();
(-r, o.reverse())
} else {
float_round_near_x(&Float::ONE, err, false, prec, rm).unwrap()
}
}
pub(crate) fn sin_cos_rational_helper(
x: &Rational,
prec: u64,
rm: RoundingMode,
) -> (Float, Float, Ordering, Ordering) {
assert_ne!(rm, Exact, "Inexact sin_cos");
let exp_x = x.floor_log_base_2_abs() + 1; if 1 - (exp_x << 1) > i64::exact_from(prec) {
let (s, o_s) = sin_rational_helper(x, prec, rm);
let (c, o_c) = cos_rational_tiny(prec, rm);
return (s, c, o_s, o_c);
}
if exp_x <= Float::MIN_EXPONENT_I64 {
fail_on_untested_path("sin_cos_rational_helper, series paths");
let (s, o_s) = sin_rational_helper(x, prec, rm);
let (c, o_c) = cos_rational_helper(x, prec, rm);
return (s, c, o_s, o_c);
}
let near_zero_threshold = max(NEAR_ZERO_MIN_CANCEL, (prec >> 1) + 1);
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)
};
if *y == 0u32 {
fail_on_untested_path("sin_cos_rational_helper, reduced argument is zero");
} else {
let (y_f, y_o) = Float::from_rational_prec_ref(y, w);
if !huge && y_o == Equal {
return sin_cos_prec_round_normal_ref(&y_f, prec, rm);
}
let (s_f, c_f, _, _) = y_f.sin_cos_round_ref(Nearest);
let exp_y = y.floor_log_base_2_abs() + 1;
let exp_s = s_f
.get_exponent()
.map_or(Float::MIN_EXPONENT_I64, i64::from);
let exp_c = c_f
.get_exponent()
.map_or(Float::MIN_EXPONENT_I64, i64::from);
let w_i = i64::exact_from(w);
let error_exp = max(exp_y - w_i, extra.unwrap_or(i64::MIN));
let bound_s = max(exp_s, error_exp) + 2;
let bound_c = max(exp_c, error_exp) + 2;
if bound_s < 0 {
let cancel = u64::exact_from(-bound_s);
if cancel >= near_zero_threshold {
let (s, o_s) = trig_rational_near_zero(y, exp_y, prec, rm, extra, w, false);
let (c, o_c) = near_one((cancel << 1) + 2, c_f < 0u32, prec, rm);
return (s, c, o_s, o_c);
}
}
if bound_c < 0 {
let cancel = u64::exact_from(-bound_c);
if cancel >= near_zero_threshold {
let (c, o_c) = trig_rational_near_zero(y, exp_y, prec, rm, extra, w, true);
let (s, o_s) = near_one((cancel << 1) + 1, s_f < 0u32, prec, rm);
return (s, c, o_s, o_c);
}
}
let mut delta_s = Rational::power_of_2(exp_s - w_i) + Rational::power_of_2(exp_y - w_i);
let mut delta_c = Rational::power_of_2(exp_c - w_i) + Rational::power_of_2(exp_y - w_i);
if let Some(extra) = extra {
let e = Rational::power_of_2(extra);
delta_s += &e;
delta_c += e;
}
let s = Rational::exact_from(&s_f);
let c = Rational::exact_from(&c_f);
if let Some((s, o_s)) = round_bracket(&(&s - &delta_s), &(s + delta_s), prec, rm)
&& let Some((c, o_c)) = round_bracket(&(&c - &delta_c), &(c + delta_c), prec, rm)
{
return (s, c, o_s, o_c);
}
}
w += increment;
increment = w >> 1;
}
}
pub(crate) const SINCOS_THRESHOLD: u64 = 25285;
fn reduce(r: &Integer, prec: u64) -> (Integer, u64) {
let l = r.significant_bits().saturating_sub(prec);
(r >> l, l)
}
fn reduce2(s: &mut Integer, c: &mut Integer, prec: u64) -> u64 {
let l = min(s.significant_bits(), c.significant_bits()).saturating_sub(prec);
*s >>= l;
*c >>= l;
l
}
const KMAX: usize = 64;
const SCRATCH_LEN: usize = 3 * KMAX;
fn sin_bs_aux(p: &Integer, r: u64, prec: u64) -> (Integer, Integer, Integer, u64) {
if *p == 0u32 {
fail_on_untested_path("sin_bs_aux, p == 0");
return (Integer::ONE, Integer::ONE, Integer::ONE, 0);
}
let p_bits = p.significant_bits();
assert!(p_bits < r || (p_bits == r && p.unsigned_abs_ref().is_power_of_2()));
let r0 = r;
let h = p.trailing_zeros().unwrap();
let pp = (p >> h).square();
let r = (r - h) << 1;
let mut scratch = vec![Integer::ZERO; SCRATCH_LEN];
split_into_chunks_mut!(scratch, KMAX, [t, q], ptoj); let mut log2_nb_terms = [0u64; KMAX];
let mut scratch_i = vec![0i64; SCRATCH_LEN];
split_into_chunks_mut!(scratch_i, KMAX, [mult, accu], size_ptoj);
let mut alloc = 2usize;
t[0] = (const { Integer::const_from_unsigned(6) } << r) - &pp;
q[0] = const { Integer::const_from_unsigned(6) };
ptoj[0] = pp.clone();
ptoj[1] = (&pp).square();
size_ptoj[1] = i64::exact_from(ptoj[1].significant_bits());
log2_nb_terms[0] = 1;
let pp_s = i64::exact_from(pp.significant_bits());
let p_s = i64::exact_from(p.significant_bits());
let r_i = i64::exact_from(r);
mult[0] = r_i - pp_s + i64::exact_from(r0) - p_s;
let prec_i = i64::exact_from(prec);
let mut k = 0usize;
let mut prec_i_have = mult[0];
let mut i = 2u64;
while prec_i_have < prec_i {
k += 1;
if k + 1 >= alloc {
assert_eq!(k + 1, alloc);
alloc += 1;
assert!(k + 1 < KMAX);
ptoj[k + 1] = (&ptoj[k]).square(); size_ptoj[k + 1] = i64::exact_from(ptoj[k + 1].significant_bits());
}
assert!(k < KMAX);
log2_nb_terms[k] = 1;
let two_i = i << 1;
q[k] = Integer::from((two_i + 2) * (two_i + 3));
t[k] = (&q[k] << r) - &pp;
q[k] *= Integer::from(two_i * (two_i + 1));
mult[k] = i64::exact_from(q[k].significant_bits()) + (r_i << 1) - size_ptoj[1] - 1;
accu[k] = if k == 0 {
mult[k]
} else {
mult[k] + accu[k - 1]
};
prec_i_have = accu[k]; let mut j = (i + 2) >> 1;
let mut l = 1usize;
while j.even() {
assert!(k >= 1);
t[k] *= &ptoj[l];
let mut tk1 = &t[k - 1] * &q[k];
tk1 <<= r << l;
tk1 += &t[k];
t[k - 1] = tk1;
let qk = q[k].clone();
q[k - 1] *= &qk;
log2_nb_terms[k - 1] += 1;
prec_i_have = i64::exact_from(qk.significant_bits());
mult[k - 1] += prec_i_have + i64::exact_from(r << l) - size_ptoj[l] - 1;
accu[k - 1] = if k == 1 {
mult[k - 1]
} else {
mult[k - 1] + accu[k - 2]
};
prec_i_have = accu[k - 1];
l += 1;
j >>= 1;
k -= 1;
}
i += 2;
}
let mut h = 0u64; while k > 0 {
t[k] *= &ptoj[usize::exact_from(log2_nb_terms[k - 1])];
let mut tk1 = &t[k - 1] * &q[k];
h += u64::power_of_2(log2_nb_terms[k]);
tk1 <<= r * h;
tk1 += &t[k];
t[k - 1] = tk1;
let qk = q[k].clone();
q[k - 1] *= qk;
k -= 1;
}
let mut m = i64::exact_from(r0) + r_i * (i64::exact_from(i) - 1);
let (q0, l) = reduce(&q[0], prec);
m += i64::exact_from(l);
let (t0, l) = reduce(&t[0], prec);
m -= i64::exact_from(l);
let (s0, l) = reduce(&(t0 * p), prec); m -= i64::exact_from(l);
let m = u64::exact_from(m);
assert!(m + q0.significant_bits() >= prec);
let c0 = Integer::from(
(((&q0).square() << (m << 1)) - (&s0).square())
.unsigned_abs()
.floor_sqrt(),
);
(q0, s0, c0, m)
}
fn sincos_aux(x: &Float, prec_s: u64) -> (Float, Float, u64) {
let mut x2 = x.clone(); let mut q_acc = Integer::ONE;
let mut l = 0i64;
let mut s_acc = Integer::ZERO; let mut c_acc = Integer::ONE; let mut sh = 1u64;
let mut j = 0u64;
while x2 != 0u32 && sh <= prec_s {
let (q2, s2, c2, l2) = if sh > prec_s >> 1 {
let (s2, e) = get_z_2exp(x2.clone()); let mut l2 = -e;
l2 += i64::exact_from(sh) - 1;
let q2 = Integer::ONE;
let c2 = Integer::power_of_2(u64::exact_from(l2));
x2 = Float::ZERO;
(q2, s2, c2, l2)
} else {
x2 <<= sh; let y = Integer::rounding_from(&x2, Down).0;
if y == 0u32 {
sh <<= 1;
j += 1;
continue;
}
let x2_prec = x2.get_prec().unwrap();
let (d, o) = x2.sub_prec_round(Float::exact_from(&y), x2_prec, Exact);
assert_eq!(o, Equal);
x2 = d;
let (q2, s2, c2, l2) = sin_bs_aux(&y, (sh << 1) - 1, prec_s);
(q2, s2, c2, i64::exact_from(l2))
};
if sh == 1 {
l = l2;
q_acc = q2;
s_acc = s2;
c_acc = c2;
} else {
let a = &s_acc + &c_acc;
let e = c_acc * &c2;
let b = c2 + &s2;
let d = s2 * &s_acc;
let t = a * b;
s_acc = t - &d - &e;
c_acc = e - d;
q_acc *= q2;
l += l2;
let (qr, lq) = reduce(&q_acc, prec_s);
q_acc = qr;
l += i64::exact_from(lq);
l -= i64::exact_from(reduce2(&mut s_acc, &mut c_acc, prec_s));
}
sh <<= 1;
j += 1;
}
let mut j = 11 * j;
let mut err = 0u64;
while j > 1 {
j = j.div_ceil(2);
err += 1;
}
let q_f = Float::exact_from(&q_acc);
let s = Float::from_integer_prec(s_acc, prec_s)
.0
.div_prec_val_ref(&q_f, prec_s)
.0
>> l;
let c = Float::from_integer_prec(c_acc, prec_s)
.0
.div_prec(q_f, prec_s)
.0
>> l;
(s, c, err)
}
pub(crate) fn sin_cos_fast(
x: &Float,
prec: u64,
rm: RoundingMode,
want_sin: bool,
want_cos: bool,
) -> (Option<(Float, Ordering)>, Option<(Float, Ordering)>) {
let mut w = prec;
w += w.ceiling_log_base_2() + 9; let mut increment = Limb::WIDTH;
let pi_over_4 = const { Float::const_from_unsigned(1686629713) } >> 31u32;
let exp_x = i64::from(x.get_exponent().unwrap());
loop {
let (ts, tc, err) = if *x > 0u32 && *x <= pi_over_4 {
sincos_aux(x, w)
} else if *x < 0u32 && *x >= -&pi_over_4 {
let (ts, tc, err) = sincos_aux(&-x, w);
(-ts, tc, err)
} else {
let pi = Float::pi_prec(if exp_x > 0 {
w + u64::exact_from(exp_x)
} else {
w
})
.0 >> 1u32; let (mut x_red, _, q) = x.ieee_remainder_and_quotient_bits_prec_ref_ref(&pi, w);
let neg = x_red < 0u32;
if neg {
x_red.neg_assign();
}
let (mut ts, mut tc, mut err) = sincos_aux(&x_red, w);
err += 1; if neg {
ts.neg_assign();
}
if q & 2 != 0 {
ts.neg_assign();
tc.neg_assign();
}
if q.odd() {
ts.neg_assign();
swap(&mut ts, &mut tc);
}
(ts, tc, err)
};
let w_i = i64::exact_from(w);
let err_i = i64::exact_from(err);
let can_round = |t: &Float| {
t.get_exponent().is_some_and(|e| {
let bits = w_i - (err_i - i64::from(e));
bits > 0
&& float_can_round(
t.significand_ref().unwrap(),
u64::exact_from(bits),
prec,
rm,
)
})
};
if (!want_sin || can_round(&ts)) && (!want_cos || can_round(&tc)) {
return (
want_sin.then(|| Float::from_float_prec_round(ts, prec, rm)),
want_cos.then(|| Float::from_float_prec_round(tc, prec, rm)),
);
}
w += increment;
increment = w >> 1;
}
}
fn sin_cos_prec_round_normal_ref(
x: &Float,
prec: u64,
rm: RoundingMode,
) -> (Float, Float, Ordering, Ordering) {
assert_ne!(rm, Exact, "Inexact sin_cos");
let exp_x = i64::from(x.get_exponent().unwrap());
let mut m = prec + prec.ceiling_log_base_2() + 13;
if exp_x < 0 {
let neg_two_exp = u64::exact_from(-(exp_x << 1));
let err_cos = neg_two_exp + 1;
if err_cos > prec + 1
&& let Some((s, o_s)) =
float_round_near_x(x, min(neg_two_exp + 2, prec + 2), false, prec, rm)
{
let (c, o_c) = near_one(err_cos, false, prec, rm);
return (s, c, o_s, o_c);
}
m += neg_two_exp;
}
if prec >= SINCOS_THRESHOLD {
let (s, c) = sin_cos_fast(x, prec, rm, true, true);
let (s, o_s) = s.unwrap();
let (c, o_c) = c.unwrap();
return (s, c, o_s, o_c);
}
sin_cos_basic(x, exp_x, m, prec, rm)
}
pub(crate) fn sin_cos_basic(
x: &Float,
exp_x: i64,
mut m: u64,
prec: u64,
rm: RoundingMode,
) -> (Float, Float, Ordering, Ordering) {
let reduce = exp_x >= 2;
let mut increment = Limb::WIDTH;
loop {
match sin_cos_ziv_step(x, exp_x, prec, rm, reduce, &mut m) {
SinCosStep::Done(s, c) => {
let (s, o_s) = Float::from_float_prec_round(s, prec, rm);
let (c, o_c) = Float::from_float_prec_round(c, prec, rm);
return (s, c, o_s, o_c);
}
SinCosStep::NearZeroCos {
cancel,
sin_negative,
} => {
let (c, o_c) = trig_near_zero(x, prec, rm, cancel, true);
let (s, o_s) = near_one((cancel << 1) + 1, sin_negative, prec, rm);
return (s, c, o_s, o_c);
}
SinCosStep::NearZeroSin {
cancel,
cos_negative,
} => {
let (s, o_s) = trig_near_zero(x, prec, rm, cancel, false);
let (c, o_c) = near_one((cancel << 1) + 2, cos_negative, prec, rm);
return (s, c, o_s, o_c);
}
SinCosStep::Retry => {}
}
m += increment;
increment = m >> 1;
}
}
fn sin_cos_turns_step(
t: &Float,
w: u64,
prec: u64,
rm: RoundingMode,
sin_near_zero: bool,
q: impl Fn() -> Rational,
) -> Option<(Float, Float, Ordering, Ordering)> {
let near_zero_threshold = max(NEAR_ZERO_MIN_CANCEL, (prec >> 1) + 1);
let w_i = i64::exact_from(w);
let err_t = i64::from(t.get_exponent().unwrap()) + 2 - w_i;
let (s, c, _, _) = t.sin_cos_prec_round_ref(w, Up);
let exp_s = i64::from(s.get_exponent().unwrap());
let exp_c = i64::from(c.get_exponent().unwrap());
let bound_s = max(exp_s, err_t) + 1;
let bound_c = max(exp_c, err_t) + 1;
if bound_s < 0 && sin_near_zero {
let cancel = u64::exact_from(-bound_s);
if cancel >= near_zero_threshold
&& let Some((s, o_s)) = trig_turns_near_zero(&q(), prec, rm, false)
{
let (c, o_c) = near_one((cancel << 1) + 2, c < 0u32, prec, rm);
return Some((s, c, o_s, o_c));
}
}
if bound_c < 0 {
let cancel = u64::exact_from(-bound_c);
if cancel >= near_zero_threshold
&& let Some((c, o_c)) = trig_turns_near_zero(&q(), prec, rm, true)
{
let (s, o_s) = near_one((cancel << 1) + 1, s < 0u32, prec, rm);
return Some((s, c, o_s, o_c));
}
}
let err_s = exp_s - err_t - 1;
let err_c = exp_c
- if err_t <= exp_c - w_i {
exp_c - w_i + 1
} else {
err_t + 1
};
if err_s > 0
&& err_c > 0
&& float_can_round(
s.significand_ref().unwrap(),
u64::exact_from(err_s),
prec,
rm,
)
&& float_can_round(
c.significand_ref().unwrap(),
u64::exact_from(err_c),
prec,
rm,
)
{
let (s, o_s) = Float::from_float_prec_round(s, prec, rm);
let (c, o_c) = Float::from_float_prec_round(c, prec, rm);
return Some((s, c, o_s, o_c));
}
None
}
pub(crate) fn sin_cos_with_period_prec_round_normal_ref(
x: &Float,
u: u64,
prec: u64,
rm: RoundingMode,
) -> (Float, Float, Ordering, 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 (
if *x < 0u32 {
Float::NEGATIVE_ZERO
} else {
Float::ZERO
},
Float::one_prec(prec),
Equal,
Equal,
);
}
xr = r;
&xr
};
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 err = ((log2u - exp_x) << 1) - 5;
if err > 0 {
let err = u64::exact_from(err);
if err > prec + 1 {
assert_ne!(rm, Exact, "Inexact sin_cos_with_period");
let (s, o_s) = sin_with_period_prec_round_normal_ref(xp, u, prec, rm);
let (c, o_c) = near_one(err, false, prec, rm);
return (s, c, o_s, o_c);
}
}
let u_bits = i64::exact_from(u.significant_bits());
if exp_x >= u_bits - 5 {
let q = Rational::exact_from(xp) / Rational::from(u);
if let Some((s, o_s)) = sin_turns_special_case(&q, prec, rm)
&& let Some((c, o_c)) = cos_turns_special_case(&q, prec, rm)
{
return (s, c, o_s, o_c);
}
}
assert_ne!(rm, Exact, "Inexact sin_cos_with_period");
if exp_x <= SCALED_INPUT_EXPONENT {
fail_on_untested_path("sin_cos_with_period_prec_round_normal_ref, tiny x/u");
let (s, o_s) = sin_with_period_prec_round_normal_ref(xp, u, prec, rm);
let (c, o_c) = cos_with_period_prec_round_normal_ref(xp, u, prec, rm);
return (s, c, o_s, o_c);
}
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);
let sin_near_zero = exp_x >= u_bits - 2;
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 let Some(result) = sin_cos_turns_step(&t, prec_t, prec, rm, sin_near_zero, || {
Rational::exact_from(xp) / Rational::from(u)
}) {
return result;
}
prec_t += increment;
increment = prec_t >> 1;
}
}
impl Float {
#[inline]
pub fn sin_cos_prec_round(
self,
prec: u64,
rm: RoundingMode,
) -> (Self, Self, Ordering, Ordering) {
self.sin_cos_prec_round_ref(prec, rm)
}
pub fn sin_cos_prec_round_ref(
&self,
prec: u64,
rm: RoundingMode,
) -> (Self, Self, Ordering, Ordering) {
assert_ne!(prec, 0);
match &self.0 {
NaN | Infinity { .. } => (Self::NAN, Self::NAN, Equal, Equal),
Zero { .. } => (self.clone(), Self::one_prec(prec), Equal, Equal),
Finite { .. } => sin_cos_prec_round_normal_ref(self, prec, rm),
}
}
#[inline]
pub fn sin_cos_prec(self, prec: u64) -> (Self, Self, Ordering, Ordering) {
self.sin_cos_prec_round_ref(prec, Nearest)
}
#[inline]
pub fn sin_cos_prec_ref(&self, prec: u64) -> (Self, Self, Ordering, Ordering) {
self.sin_cos_prec_round_ref(prec, Nearest)
}
#[inline]
pub fn sin_cos_round(self, rm: RoundingMode) -> (Self, Self, Ordering, Ordering) {
let prec = self.significant_bits();
self.sin_cos_prec_round_ref(prec, rm)
}
#[inline]
pub fn sin_cos_round_ref(&self, rm: RoundingMode) -> (Self, Self, Ordering, Ordering) {
self.sin_cos_prec_round_ref(self.significant_bits(), rm)
}
#[inline]
pub fn sin_cos_prec_round_assign(
&mut self,
cos: &mut Self,
prec: u64,
rm: RoundingMode,
) -> (Ordering, Ordering) {
let (s, c, o_s, o_c) = self.sin_cos_prec_round_ref(prec, rm);
*self = s;
*cos = c;
(o_s, o_c)
}
#[inline]
pub fn sin_cos_prec_assign(&mut self, cos: &mut Self, prec: u64) -> (Ordering, Ordering) {
self.sin_cos_prec_round_assign(cos, prec, Nearest)
}
#[inline]
pub fn sin_cos_round_assign(
&mut self,
cos: &mut Self,
rm: RoundingMode,
) -> (Ordering, Ordering) {
let prec = self.significant_bits();
self.sin_cos_prec_round_assign(cos, prec, rm)
}
}
impl Float {
#[inline]
#[allow(clippy::needless_pass_by_value)]
pub fn sin_cos_rational_prec_round(
x: Rational,
prec: u64,
rm: RoundingMode,
) -> (Self, Self, Ordering, Ordering) {
Self::sin_cos_rational_prec_round_ref(&x, prec, rm)
}
pub fn sin_cos_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);
}
sin_cos_rational_helper(x, prec, rm)
}
#[inline]
#[allow(clippy::needless_pass_by_value)]
pub fn sin_cos_rational_prec(x: Rational, prec: u64) -> (Self, Self, Ordering, Ordering) {
Self::sin_cos_rational_prec_round_ref(&x, prec, Nearest)
}
#[inline]
pub fn sin_cos_rational_prec_ref(x: &Rational, prec: u64) -> (Self, Self, Ordering, Ordering) {
Self::sin_cos_rational_prec_round_ref(x, prec, Nearest)
}
}
pub(crate) fn sin_cos_turns_helper(
q: &Rational,
prec: u64,
rm: RoundingMode,
) -> (Float, Float, Ordering, 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 sin_cos_with_period");
let (s, o_s) = sin_turns_helper(q, prec, rm);
let (c, o_c) = near_one(err, false, prec, rm);
return (s, c, o_s, o_c);
}
}
if exp_q >= -4
&& let Some((s, o_s)) = sin_turns_special_case(q, prec, rm)
&& let Some((c, o_c)) = cos_turns_special_case(q, prec, rm)
{
return (s, c, o_s, o_c);
}
assert_ne!(rm, Exact, "Inexact sin_cos_with_period");
if exp_q <= SCALED_INPUT_EXPONENT {
fail_on_untested_path("sin_cos_turns_helper, tiny q");
let (s, o_s) = sin_turns_helper(q, prec, rm);
let (c, o_c) = cos_turns_helper(q, prec, rm);
return (s, c, o_s, o_c);
}
let mut w = prec + prec.ceiling_log_base_2() + 8;
let mut increment = Limb::WIDTH;
let sin_near_zero = exp_q >= -2;
loop {
let t = (Float::pi_prec(w).0 << 1u32)
.mul_prec(Float::from_rational_prec_ref(q, w).0, w)
.0;
if let Some(result) = sin_cos_turns_step(&t, w, prec, rm, sin_near_zero, || q.clone()) {
return result;
}
w += increment;
increment = w >> 1;
}
}
impl Float {
#[inline]
pub fn sin_cos_with_period_prec_round(
self,
u: u64,
prec: u64,
rm: RoundingMode,
) -> (Self, Self, Ordering, Ordering) {
self.sin_cos_with_period_prec_round_ref(u, prec, rm)
}
pub fn sin_cos_with_period_prec_round_ref(
&self,
u: u64,
prec: u64,
rm: RoundingMode,
) -> (Self, Self, Ordering, Ordering) {
assert_ne!(prec, 0);
match &self.0 {
_ if u == 0 => (Self::NAN, Self::NAN, Equal, Equal),
NaN | Infinity { .. } => (Self::NAN, Self::NAN, Equal, Equal),
Zero { .. } => (self.clone(), Self::one_prec(prec), Equal, Equal),
Finite { .. } => sin_cos_with_period_prec_round_normal_ref(self, u, prec, rm),
}
}
#[inline]
pub fn sin_cos_with_period_prec(self, u: u64, prec: u64) -> (Self, Self, Ordering, Ordering) {
self.sin_cos_with_period_prec_round_ref(u, prec, Nearest)
}
#[inline]
pub fn sin_cos_with_period_prec_ref(
&self,
u: u64,
prec: u64,
) -> (Self, Self, Ordering, Ordering) {
self.sin_cos_with_period_prec_round_ref(u, prec, Nearest)
}
#[inline]
pub fn sin_cos_with_period_round(
self,
u: u64,
rm: RoundingMode,
) -> (Self, Self, Ordering, Ordering) {
let prec = self.significant_bits();
self.sin_cos_with_period_prec_round_ref(u, prec, rm)
}
#[inline]
pub fn sin_cos_with_period_round_ref(
&self,
u: u64,
rm: RoundingMode,
) -> (Self, Self, Ordering, Ordering) {
self.sin_cos_with_period_prec_round_ref(u, self.significant_bits(), rm)
}
#[inline]
pub fn sin_cos_with_period(self, u: u64) -> (Self, Self) {
let prec = self.significant_bits();
let (s, c, _, _) = self.sin_cos_with_period_prec(u, prec);
(s, c)
}
#[inline]
pub fn sin_cos_with_period_ref(&self, u: u64) -> (Self, Self) {
let (s, c, _, _) = self.sin_cos_with_period_prec_ref(u, self.significant_bits());
(s, c)
}
#[inline]
pub fn sin_cos_with_period_prec_round_assign(
&mut self,
cos: &mut Self,
u: u64,
prec: u64,
rm: RoundingMode,
) -> (Ordering, Ordering) {
let (s, c, o_s, o_c) = self.sin_cos_with_period_prec_round_ref(u, prec, rm);
*self = s;
*cos = c;
(o_s, o_c)
}
#[inline]
pub fn sin_cos_with_period_prec_assign(
&mut self,
cos: &mut Self,
u: u64,
prec: u64,
) -> (Ordering, Ordering) {
self.sin_cos_with_period_prec_round_assign(cos, u, prec, Nearest)
}
#[inline]
pub fn sin_cos_with_period_round_assign(
&mut self,
cos: &mut Self,
u: u64,
rm: RoundingMode,
) -> (Ordering, Ordering) {
let prec = self.significant_bits();
self.sin_cos_with_period_prec_round_assign(cos, u, prec, rm)
}
#[inline]
pub fn sin_cos_with_period_assign(&mut self, cos: &mut Self, u: u64) {
let prec = self.significant_bits();
self.sin_cos_with_period_prec_assign(cos, u, prec);
}
}
impl Float {
#[inline]
#[allow(clippy::needless_pass_by_value)]
pub fn sin_cos_with_period_rational_prec_round(
x: Rational,
u: u64,
prec: u64,
rm: RoundingMode,
) -> (Self, Self, Ordering, Ordering) {
Self::sin_cos_with_period_rational_prec_round_ref(&x, u, prec, rm)
}
pub fn sin_cos_with_period_rational_prec_round_ref(
x: &Rational,
u: u64,
prec: u64,
rm: RoundingMode,
) -> (Self, Self, Ordering, Ordering) {
assert_ne!(prec, 0);
if u == 0 {
return (Self::NAN, Self::NAN, Equal, Equal);
}
if *x == 0u32 {
return (Self::ZERO, Self::one_prec(prec), Equal, Equal);
}
let q = x / Rational::from(u) % Rational::ONE;
if q == 0u32 {
return (
if *x < 0u32 {
Self::NEGATIVE_ZERO
} else {
Self::ZERO
},
Self::one_prec(prec),
Equal,
Equal,
);
}
sin_cos_turns_helper(&q, prec, rm)
}
#[inline]
#[allow(clippy::needless_pass_by_value)]
pub fn sin_cos_with_period_rational_prec(
x: Rational,
u: u64,
prec: u64,
) -> (Self, Self, Ordering, Ordering) {
Self::sin_cos_with_period_rational_prec_round_ref(&x, u, prec, Nearest)
}
#[inline]
pub fn sin_cos_with_period_rational_prec_ref(
x: &Rational,
u: u64,
prec: u64,
) -> (Self, Self, Ordering, Ordering) {
Self::sin_cos_with_period_rational_prec_round_ref(x, u, prec, Nearest)
}
}
impl Float {
#[inline]
pub fn sin_cos_pi_prec_round(
self,
prec: u64,
rm: RoundingMode,
) -> (Self, Self, Ordering, Ordering) {
self.sin_cos_with_period_prec_round(2, prec, rm)
}
#[inline]
pub fn sin_cos_pi_prec_round_ref(
&self,
prec: u64,
rm: RoundingMode,
) -> (Self, Self, Ordering, Ordering) {
self.sin_cos_with_period_prec_round_ref(2, prec, rm)
}
#[inline]
pub fn sin_cos_pi_prec(self, prec: u64) -> (Self, Self, Ordering, Ordering) {
self.sin_cos_with_period_prec(2, prec)
}
#[inline]
pub fn sin_cos_pi_prec_ref(&self, prec: u64) -> (Self, Self, Ordering, Ordering) {
self.sin_cos_with_period_prec_ref(2, prec)
}
#[inline]
pub fn sin_cos_pi_round(self, rm: RoundingMode) -> (Self, Self, Ordering, Ordering) {
self.sin_cos_with_period_round(2, rm)
}
#[inline]
pub fn sin_cos_pi_round_ref(&self, rm: RoundingMode) -> (Self, Self, Ordering, Ordering) {
self.sin_cos_with_period_round_ref(2, rm)
}
#[inline]
pub fn sin_cos_pi(self) -> (Self, Self) {
let prec = self.significant_bits();
let (s, c, _, _) = self.sin_cos_pi_prec(prec);
(s, c)
}
#[inline]
pub fn sin_cos_pi_ref(&self) -> (Self, Self) {
let (s, c, _, _) = self.sin_cos_pi_prec_ref(self.significant_bits());
(s, c)
}
#[inline]
pub fn sin_cos_pi_prec_round_assign(
&mut self,
cos: &mut Self,
prec: u64,
rm: RoundingMode,
) -> (Ordering, Ordering) {
self.sin_cos_with_period_prec_round_assign(cos, 2, prec, rm)
}
#[inline]
pub fn sin_cos_pi_prec_assign(&mut self, cos: &mut Self, prec: u64) -> (Ordering, Ordering) {
self.sin_cos_with_period_prec_assign(cos, 2, prec)
}
#[inline]
pub fn sin_cos_pi_round_assign(
&mut self,
cos: &mut Self,
rm: RoundingMode,
) -> (Ordering, Ordering) {
self.sin_cos_with_period_round_assign(cos, 2, rm)
}
#[inline]
pub fn sin_cos_pi_assign(&mut self, cos: &mut Self) {
let prec = self.significant_bits();
self.sin_cos_pi_prec_assign(cos, prec);
}
}
impl Float {
#[inline]
#[allow(clippy::needless_pass_by_value)]
pub fn sin_cos_pi_rational_prec_round(
x: Rational,
prec: u64,
rm: RoundingMode,
) -> (Self, Self, Ordering, Ordering) {
Self::sin_cos_with_period_rational_prec_round_ref(&x, 2, prec, rm)
}
#[inline]
pub fn sin_cos_pi_rational_prec_round_ref(
x: &Rational,
prec: u64,
rm: RoundingMode,
) -> (Self, Self, Ordering, Ordering) {
Self::sin_cos_with_period_rational_prec_round_ref(x, 2, prec, rm)
}
#[inline]
#[allow(clippy::needless_pass_by_value)]
pub fn sin_cos_pi_rational_prec(x: Rational, prec: u64) -> (Self, Self, Ordering, Ordering) {
Self::sin_cos_with_period_rational_prec_ref(&x, 2, prec)
}
#[inline]
pub fn sin_cos_pi_rational_prec_ref(
x: &Rational,
prec: u64,
) -> (Self, Self, Ordering, Ordering) {
Self::sin_cos_with_period_rational_prec_ref(x, 2, prec)
}
}
impl SinCos for Float {
type Output = Self;
#[inline]
fn sin_cos(self) -> (Self, Self) {
let prec = self.significant_bits();
let (s, c, _, _) = self.sin_cos_prec_round_ref(prec, Nearest);
(s, c)
}
}
impl SinCos for &Float {
type Output = Float;
#[inline]
fn sin_cos(self) -> (Float, Float) {
let (s, c, _, _) = self.sin_cos_prec_round_ref(self.significant_bits(), Nearest);
(s, c)
}
}
impl SinCosAssign for Float {
#[inline]
fn sin_cos_assign(&mut self, cos: &mut Self) {
let prec = self.significant_bits();
self.sin_cos_prec_round_assign(cos, prec, Nearest);
}
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_sin_cos<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::sin_cos_prec, x)
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_sin_cos_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::sin_cos_rational_prec_ref, x)
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_sin_cos_with_period<T: PrimitiveFloat>(x: T, u: u64) -> (T, T)
where
Float: From<T> + PartialOrd<T>,
for<'a> T: ExactFrom<&'a Float> + RoundingFrom<&'a Float>,
{
emulate_float_to_float_pair_fn(|x, prec| Float::sin_cos_with_period_prec(x, u, prec), x)
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
#[cfg_attr(dylint_lib = "malachite_lints", expect(long_lines))]
pub fn primitive_float_sin_cos_with_period_rational<T: PrimitiveFloat>(
x: &Rational,
u: u64,
) -> (T, T)
where
Float: PartialOrd<T>,
for<'a> T: ExactFrom<&'a Float> + RoundingFrom<&'a Float>,
{
emulate_rational_to_float_pair_fn(
|x, prec| Float::sin_cos_with_period_rational_prec_ref(x, u, prec),
x,
)
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_sin_cos_pi<T: PrimitiveFloat>(x: T) -> (T, T)
where
Float: From<T> + PartialOrd<T>,
for<'a> T: ExactFrom<&'a Float> + RoundingFrom<&'a Float>,
{
primitive_float_sin_cos_with_period(x, 2)
}
#[inline]
#[allow(clippy::type_repetition_in_bounds)]
pub fn primitive_float_sin_cos_pi_rational<T: PrimitiveFloat>(x: &Rational) -> (T, T)
where
Float: PartialOrd<T>,
for<'a> T: ExactFrom<&'a Float> + RoundingFrom<&'a Float>,
{
primitive_float_sin_cos_with_period_rational(x, 2)
}