use crate::arithmetic::fp_mul_i_fast;
use crate::constants::*;
use crate::error::SolMathError;
const MAX_TRIG_ANGLE: i128 = 10_000_000_000_000_000_000_000_000;
#[inline]
fn validate_angle(x: i128) -> Result<(), SolMathError> {
if x.unsigned_abs() > MAX_TRIG_ANGLE as u128 {
Err(SolMathError::DomainError)
} else {
Ok(())
}
}
pub(crate) fn sin_core(x: i128) -> Result<i128, SolMathError> {
let t = fp_mul_i_fast(x, x);
let mut r = SIN_C11;
r = fp_mul_i_fast(r, t) + SIN_C9;
r = fp_mul_i_fast(r, t) + SIN_C7;
r = fp_mul_i_fast(r, t) + SIN_C5;
r = fp_mul_i_fast(r, t) + SIN_C3;
r = fp_mul_i_fast(r, t) + SIN_C1;
Ok(fp_mul_i_fast(r, x))
}
pub(crate) fn cos_core(x: i128) -> Result<i128, SolMathError> {
let t = fp_mul_i_fast(x, x);
let mut r = COS_C10;
r = fp_mul_i_fast(r, t) + COS_C8;
r = fp_mul_i_fast(r, t) + COS_C6;
r = fp_mul_i_fast(r, t) + COS_C4;
r = fp_mul_i_fast(r, t) + COS_C2;
r = fp_mul_i_fast(r, t) + COS_C0;
Ok(r)
}
pub(crate) fn reduce_mod_2pi(x: i128) -> i128 {
let mut r = x.rem_euclid(TWO_PI_SCALE);
let mut q = x.div_euclid(TWO_PI_SCALE);
while q != 0 {
let corr = q * (-TWO_PI_LO); let corr_ulp = if corr >= 0 {
(corr + SCALE_I / 2) / SCALE_I
} else {
(corr - SCALE_I / 2) / SCALE_I
};
if corr_ulp == 0 {
break;
}
let t = r + corr_ulp;
r = t.rem_euclid(TWO_PI_SCALE);
q = t.div_euclid(TWO_PI_SCALE);
}
if r > PI_SCALE {
r -= TWO_PI_SCALE;
}
r
}
pub fn sin_fixed(x: i128) -> Result<i128, SolMathError> {
validate_angle(x)?;
let mut xx = reduce_mod_2pi(x);
let sign = if xx < 0 {
xx = -xx;
-1i128
} else {
1i128
};
if xx > PI_OVER_2_SCALE {
xx = PI_SCALE - xx;
}
if xx > PI_OVER_4_SCALE {
Ok(cos_core(PI_OVER_2_SCALE - xx)? * sign)
} else {
Ok(sin_core(xx)? * sign)
}
}
pub fn cos_fixed(x: i128) -> Result<i128, SolMathError> {
validate_angle(x)?;
let mut xx = reduce_mod_2pi(x);
xx = if xx < 0 { -xx } else { xx };
let cos_sign = if xx > PI_OVER_2_SCALE {
xx = PI_SCALE - xx;
-1i128
} else {
1i128
};
if xx > PI_OVER_4_SCALE {
Ok(sin_core(PI_OVER_2_SCALE - xx)? * cos_sign)
} else {
Ok(cos_core(xx)? * cos_sign)
}
}
pub fn sincos_fixed(x: i128) -> Result<(i128, i128), SolMathError> {
validate_angle(x)?;
let mut xx = reduce_mod_2pi(x);
let sin_sign = if xx < 0 {
xx = -xx;
-1i128
} else {
1i128
};
let cos_sign = if xx > PI_OVER_2_SCALE {
xx = PI_SCALE - xx;
-1i128
} else {
1i128
};
if xx > PI_OVER_4_SCALE {
let y = PI_OVER_2_SCALE - xx;
Ok((cos_core(y)? * sin_sign, sin_core(y)? * cos_sign))
} else {
Ok((sin_core(xx)? * sin_sign, cos_core(xx)? * cos_sign))
}
}