use crate::{BigInt, Ratio, pi_iter};
pub fn sin_iter(x: &Ratio, iter: usize) -> Ratio {
let pi = pi_iter(iter);
let mut pi_div = x.div_rat(&pi);
let mut negate = false;
if pi_div.is_neg() {
negate = !negate;
pi_div.neg_mut();
}
if pi_div.gt_i32(2) {
pi_div.sub_bi_mut(&pi_div.truncate_bi().div_i32(2).mul_i32(2));
}
if pi_div.gt_one() {
pi_div.sub_i32_mut(1);
negate = !negate;
}
if pi_div.mul_i32(2).gt_one() {
pi_div = Ratio::one().sub_rat(&pi_div);
}
if pi_div.mul_i32(4).lt_one() {
let mut result = sin_iter_worker(&pi_div.mul_rat(&pi), iter);
if negate { result.neg_mut(); }
result
}
else {
let mut result = cos_iter_worker(&Ratio::from_denom_and_numer_i32(2, 1).sub_rat(&pi_div).mul_rat(&pi), iter);
if negate { result.neg_mut(); }
result
}
}
fn sin_iter_worker(x: &Ratio, iter: usize) -> Ratio {
let mut result = x.clone();
let mut numer = x.pow_i32(3);
let x_sq = x.mul_rat(x);
let mut denom = BigInt::from_i32(6);
for i in 0..iter {
result.sub_rat_mut(
&numer.div_bi(&denom)
);
denom.mul_i32_mut((i * 4 + 4) as i32);
denom.mul_i32_mut((i * 4 + 5) as i32);
numer.mul_rat_mut(&x_sq);
result.add_rat_mut(
&numer.div_bi(&denom)
);
denom.mul_i32_mut((i * 4 + 6) as i32);
denom.mul_i32_mut((i * 4 + 7) as i32);
numer.mul_rat_mut(&x_sq);
}
result
}
pub fn cos_iter(x: &Ratio, iter: usize) -> Ratio {
let pi = pi_iter(iter);
let mut pi_div = x.div_rat(&pi);
let mut negate = false;
if pi_div.is_neg() {
pi_div.neg_mut();
}
if pi_div.gt_i32(2) {
pi_div.sub_bi_mut(&pi_div.truncate_bi().div_i32(2).mul_i32(2));
}
if pi_div.gt_one() {
pi_div = Ratio::from_i32(2).sub_rat(&pi_div);
}
if pi_div.mul_i32(2).gt_one() {
pi_div = Ratio::one().sub_rat(&pi_div);
negate = !negate;
}
if pi_div.mul_i32(4).lt_one() {
let mut result = cos_iter_worker(&pi_div.mul_rat(&pi), iter);
if negate { result.neg_mut(); }
result
}
else {
let mut result = sin_iter_worker(&Ratio::from_denom_and_numer_i32(2, 1).sub_rat(&pi_div).mul_rat(&pi), iter);
if negate { result.neg_mut(); }
result
}
}
fn cos_iter_worker(x: &Ratio, iter: usize) -> Ratio {
let mut result = Ratio::one();
let x_sq = x.mul_rat(x);
let mut numer = x_sq.clone();
let mut denom = BigInt::from_i32(2);
for i in 0..iter {
result.sub_rat_mut(
&numer.div_bi(&denom)
);
denom.mul_i32_mut((i * 4 + 3) as i32);
denom.mul_i32_mut((i * 4 + 4) as i32);
numer.mul_rat_mut(&x_sq);
result.add_rat_mut(
&numer.div_bi(&denom)
);
denom.mul_i32_mut((i * 4 + 5) as i32);
denom.mul_i32_mut((i * 4 + 6) as i32);
numer.mul_rat_mut(&x_sq);
}
result
}
pub fn tan_iter(x: &Ratio, iter: usize) -> Ratio {
sin_iter(x, iter).div_rat(&cos_iter(x, iter))
}
#[cfg(test)]
mod tests {
use crate::{Ratio, sqrt_iter, sin_iter, cos_iter, pi_iter};
use crate::utils::are_close;
use crate::consts::RUN_ALL_TESTS;
#[test]
fn sin_test() {
if !RUN_ALL_TESTS { return; }
let iter = 9;
let samples = vec![
(-2, 12, Ratio::from_denom_and_numer_i32(2, 1).neg()), (-1, 12, sqrt_iter(&Ratio::from_i32(6), iter).sub_rat(&sqrt_iter(&Ratio::from_i32(2), iter)).div_i32(4).neg()), ( 0, 12, Ratio::zero()), ( 1, 12, sqrt_iter(&Ratio::from_i32(6), iter).sub_rat(&sqrt_iter(&Ratio::from_i32(2), iter)).div_i32(4)), ( 2, 12, Ratio::from_denom_and_numer_i32(2, 1)), ( 3, 12, sqrt_iter(&Ratio::from_i32(2), iter).reci()), ( 4, 12, sqrt_iter(&Ratio::from_i32(3), iter).div_i32(2)), ( 5, 12, sqrt_iter(&Ratio::from_i32(6), iter).add_rat(&sqrt_iter(&Ratio::from_i32(2), iter)).div_i32(4)), ( 6, 12, Ratio::one()), ( 7, 12, sqrt_iter(&Ratio::from_i32(6), iter).add_rat(&sqrt_iter(&Ratio::from_i32(2), iter)).div_i32(4)), ( 8, 12, sqrt_iter(&Ratio::from_i32(3), iter).div_i32(2)), ( 9, 12, sqrt_iter(&Ratio::from_i32(2), iter).reci()), (10, 12, Ratio::from_denom_and_numer_i32(2, 1)), (11, 12, sqrt_iter(&Ratio::from_i32(6), iter).sub_rat(&sqrt_iter(&Ratio::from_i32(2), iter)).div_i32(4)), (12, 12, Ratio::zero()), (13, 12, sqrt_iter(&Ratio::from_i32(6), iter).sub_rat(&sqrt_iter(&Ratio::from_i32(2), iter)).div_i32(4).neg()), (14, 12, Ratio::from_denom_and_numer_i32(2, -1)), ];
let pi = pi_iter(iter);
let cos_coeff = Ratio::from_denom_and_numer_i32(-2, 1);
let accuracy = 3e-6;
for (numer, denom, value) in samples.into_iter() {
let sin_val = sin_iter(&Ratio::from_denom_and_numer_i32(denom, numer).mul_rat(&pi), iter);
let sin_val2 = sin_iter(&Ratio::from_denom_and_numer_i32(denom, numer).add_i32(6).mul_rat(&pi), iter);
let sin_val3 = sin_iter(&Ratio::from_denom_and_numer_i32(denom, numer).add_i32(-6).mul_rat(&pi), iter);
let cos_val = cos_iter(&Ratio::from_denom_and_numer_i32(denom, numer).add_rat(&cos_coeff).mul_rat(&pi), iter);
let cos_val2 = cos_iter(&Ratio::from_denom_and_numer_i32(denom, numer).add_rat(&cos_coeff).add_i32(6).mul_rat(&pi), iter);
let cos_val3 = cos_iter(&Ratio::from_denom_and_numer_i32(denom, numer).add_rat(&cos_coeff).add_i32(-6).mul_rat(&pi), iter);
let ans_f64 = (3.14159265358979f64 * numer as f64 / denom as f64).sin();
let ans_f64 = Ratio::from_ieee754_f64(ans_f64).unwrap();
assert!(are_close(&ans_f64, &value, accuracy));
assert!(are_close(&sin_val, &value, accuracy));
assert!(are_close(&sin_val2, &value, accuracy));
assert!(are_close(&sin_val3, &value, accuracy));
assert!(are_close(&cos_val, &value, accuracy));
assert!(are_close(&cos_val2, &value, accuracy));
assert!(are_close(&cos_val3, &value, accuracy));
}
}
}