use crate::common::consts::FOUR;
use crate::common::consts::THREE;
use crate::common::consts::TRIG_EXP_THRES;
use crate::common::util::bump_prec_retry;
use crate::common::util::calc_add_cost;
use crate::common::util::calc_mul_cost;
use crate::common::util::round_p;
use crate::defs::Error;
use crate::defs::RoundingMode;
use crate::num::ExactNumNumber;
use crate::ops::consts::Consts;
use crate::ops::series::series_cost_optimize;
use crate::ops::series::series_run;
use crate::ops::series::ArgReductionEstimator;
use crate::ops::series::FactPolycoeffGen;
use crate::ops::util::compute_small_exp;
use crate::WORD_BIT_SIZE;
struct SinArgReductionEstimator {}
impl ArgReductionEstimator for SinArgReductionEstimator {
fn reduction_cost(n: usize, p: usize) -> u64 {
let cost_mul = calc_mul_cost(p);
let cost_add = calc_add_cost(p);
let cost_mul2 = calc_mul_cost(THREE.mantissa_max_bit_len());
n as u64 * (2 * cost_mul + 3 * cost_add + cost_mul2) as u64
}
#[inline]
fn reduction_effect(n: usize, m: isize) -> usize {
((n as isize) * 1000 / 631 + m) as usize
}
}
impl ExactNumNumber {
pub fn sin(&self, p: usize, rm: RoundingMode, cc: &mut Consts) -> Result<Self, Error> {
let p = round_p(p);
if self.is_zero() {
return Self::new2(p, self.sign(), self.inexact());
}
let mut p_inc = WORD_BIT_SIZE;
let mut p_wrk = p.max(self.mantissa_max_bit_len());
compute_small_exp!(self, self.exponent() as isize * 2 - 2, true, p_wrk, p, rm);
p_wrk += p_inc;
let mut add_p = (3 - TRIG_EXP_THRES) as usize;
loop {
let mut x = self.clone()?;
let p_x = p_wrk + add_p;
x.set_precision(p_x, RoundingMode::None)?;
x = x.reduce_trig_arg(cc, RoundingMode::None)?;
let (t, q) = x.trig_arg_pi_proximity(cc, RoundingMode::None)?;
if q & 1 == 0 && add_p < t {
add_p = t;
} else {
let mut ret = x.sin_series(RoundingMode::None)?;
if ret.try_set_precision(p, rm, p_wrk)? {
ret.set_inexact(ret.inexact() | self.inexact());
break Ok(ret);
}
bump_prec_retry(&mut p_wrk, &mut p_inc, p)?;
}
}
}
pub fn sin_series(mut self, rm: RoundingMode) -> Result<Self, Error> {
let p = self.mantissa_max_bit_len();
let mut polycoeff_gen = FactPolycoeffGen::for_sin(p)?;
let (reduction_times, niter, e_eff) = series_cost_optimize::<SinArgReductionEstimator>(
p,
&polycoeff_gen,
-(self.exponent() as isize),
2,
false,
);
let add_prec = reduction_times as isize * 6 + 9 - e_eff as isize;
let p_arg = p + if add_prec > 0 { add_prec as usize } else { 0 };
self.set_precision(p_arg, rm)?;
let arg = if reduction_times > 0 {
self.sin_arg_reduce(reduction_times, rm)?
} else {
self
};
let acc = arg.clone()?; let x_step = arg.mul(&arg, p_arg, rm)?; let x_first = arg.mul(&x_step, p_arg, rm)?;
let ret = series_run(acc, x_first, x_step, niter, &mut polycoeff_gen)?;
if reduction_times > 0 {
ret.sin_arg_restore(reduction_times, rm)
} else {
Ok(ret)
}
}
fn sin_arg_reduce(&self, n: usize, rm: RoundingMode) -> Result<Self, Error> {
let mut d = THREE.clone()?;
for _ in 1..n {
d = d.mul_full_prec(&THREE)?;
}
self.div(&d, self.mantissa_max_bit_len(), rm)
}
fn sin_arg_restore(&self, n: usize, rm: RoundingMode) -> Result<Self, Error> {
let mut sin = self.clone()?;
let p = sin.mantissa_max_bit_len();
for _ in 0..n {
let mut sin_cub = sin.mul(&sin, p, rm)?;
sin_cub = sin_cub.mul(&sin, p, rm)?;
let p1 = sin.mul(&THREE, p, rm)?;
let p2 = sin_cub.mul(&FOUR, p, rm)?;
sin = p1.sub(&p2, p, rm)?;
}
Ok(sin)
}
}
#[cfg(test)]
mod tests {
use crate::common::util::random_subnormal;
use super::*;
#[test]
fn test_sine() {
let p = 320;
let mut cc = Consts::new().unwrap();
let rm = RoundingMode::ToEven;
let mut n1 = ExactNumNumber::from_word(5, p).unwrap();
n1.set_exponent(0);
let _n2 = n1.sin(p, rm, &mut cc).unwrap();
let mut half_pi = cc.pi_num(128, RoundingMode::None).unwrap();
half_pi.set_exponent(1);
half_pi.set_precision(p, RoundingMode::None).unwrap();
let n2 = half_pi.sin(p, rm, &mut cc).unwrap();
let n3 = ExactNumNumber::parse(
"F.FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2DC85F7E77EC487_e-1",
crate::Radix::Hex,
640,
RoundingMode::None,
&mut cc,
)
.unwrap();
assert!(n2.cmp(&n3) == 0);
half_pi.set_exponent(256);
let n2 = half_pi.sin(p, rm, &mut cc).unwrap();
let n3 = ExactNumNumber::parse(
"F.AE195882CABDC16FAF2A733AB99159AF50C47E8B9ED8BA42C5872FF88A52726061B4231170F1BE8_e-1",
crate::Radix::Hex,
640,
RoundingMode::None,
&mut cc,
)
.unwrap();
assert!(n2.cmp(&n3) == 0);
let p = 384;
let d1 = ExactNumNumber::parse("-4.6C7B4339E519CAFC9C58869B348D66FD510B4E74F0A6476126B55FD73F5533C7B84C5733C34BB312677AE7C015A666D0_e-17", crate::Radix::Hex, p, RoundingMode::None, &mut cc).unwrap();
let d2 = d1.sin(p, RoundingMode::ToEven, &mut cc).unwrap();
let d3 = ExactNumNumber::parse("-4.6C7B4339E519CAFC9C58869B348D66FD510B4E74F0A638F3725423CE43E0D375A6FEE810D77915E6EA2D8EE32EBCF618_e-17", crate::Radix::Hex, p, RoundingMode::None, &mut cc).unwrap();
assert!(d2.cmp(&d3) == 0);
let d3 = ExactNumNumber::min_positive(p).unwrap();
let zero = ExactNumNumber::new(1).unwrap();
let n1 = random_subnormal(p);
assert!(d3.sin(p, rm, &mut cc).unwrap().cmp(&d3) == 0);
assert!(zero.sin(p, rm, &mut cc).unwrap().is_zero());
assert!(n1.sin(p, rm, &mut cc).unwrap().cmp(&n1) == 0);
}
}