use crate::core_type::I128;
impl<const SCALE: u32> I128<SCALE> {
#[inline]
#[must_use]
pub fn sin(self) -> Self {
Self::from_f64_lossy(self.to_f64_lossy().sin())
}
#[inline]
#[must_use]
pub fn cos(self) -> Self {
Self::from_f64_lossy(self.to_f64_lossy().cos())
}
#[inline]
#[must_use]
pub fn tan(self) -> Self {
Self::from_f64_lossy(self.to_f64_lossy().tan())
}
#[inline]
#[must_use]
pub fn asin(self) -> Self {
Self::from_f64_lossy(self.to_f64_lossy().asin())
}
#[inline]
#[must_use]
pub fn acos(self) -> Self {
Self::from_f64_lossy(self.to_f64_lossy().acos())
}
#[inline]
#[must_use]
pub fn atan(self) -> Self {
Self::from_f64_lossy(self.to_f64_lossy().atan())
}
#[inline]
#[must_use]
pub fn atan2(self, other: Self) -> Self {
Self::from_f64_lossy(self.to_f64_lossy().atan2(other.to_f64_lossy()))
}
#[inline]
#[must_use]
pub fn sinh(self) -> Self {
Self::from_f64_lossy(self.to_f64_lossy().sinh())
}
#[inline]
#[must_use]
pub fn cosh(self) -> Self {
Self::from_f64_lossy(self.to_f64_lossy().cosh())
}
#[inline]
#[must_use]
pub fn tanh(self) -> Self {
Self::from_f64_lossy(self.to_f64_lossy().tanh())
}
#[inline]
#[must_use]
pub fn asinh(self) -> Self {
Self::from_f64_lossy(self.to_f64_lossy().asinh())
}
#[inline]
#[must_use]
pub fn acosh(self) -> Self {
Self::from_f64_lossy(self.to_f64_lossy().acosh())
}
#[inline]
#[must_use]
pub fn atanh(self) -> Self {
Self::from_f64_lossy(self.to_f64_lossy().atanh())
}
#[inline]
#[must_use]
pub fn to_degrees(self) -> Self {
Self::from_f64_lossy(self.to_f64_lossy().to_degrees())
}
#[inline]
#[must_use]
pub fn to_radians(self) -> Self {
Self::from_f64_lossy(self.to_f64_lossy().to_radians())
}
}
#[cfg(test)]
mod tests {
use crate::consts::DecimalConsts;
use crate::core_type::I128s12;
const TWO_LSB: i128 = 2;
const FOUR_LSB: i128 = 4;
const ANGLE_TOLERANCE_LSB: i128 = 32;
fn within_lsb(actual: I128s12, expected: I128s12, lsb: i128) -> bool {
let diff = (actual.to_bits() - expected.to_bits()).abs();
diff <= lsb
}
#[test]
fn sin_zero_is_zero() {
assert_eq!(I128s12::ZERO.sin(), I128s12::ZERO);
}
#[test]
fn cos_zero_is_one() {
assert_eq!(I128s12::ZERO.cos(), I128s12::ONE);
}
#[test]
fn tan_zero_is_zero() {
assert_eq!(I128s12::ZERO.tan(), I128s12::ZERO);
}
#[test]
fn sin_squared_plus_cos_squared_is_one() {
for raw in [
1_234_567_890_123_i128, -2_345_678_901_234_i128, 500_000_000_000_i128, -500_000_000_000_i128, 4_567_891_234_567_i128, ] {
let x = I128s12::from_bits(raw);
let s = x.sin();
let c = x.cos();
let sum = (s * s) + (c * c);
assert!(
within_lsb(sum, I128s12::ONE, FOUR_LSB),
"sin^2 + cos^2 != 1 for raw={raw}: got bits {} (delta {})",
sum.to_bits(),
(sum.to_bits() - I128s12::ONE.to_bits()).abs(),
);
}
}
#[test]
fn asin_zero_is_zero() {
assert_eq!(I128s12::ZERO.asin(), I128s12::ZERO);
}
#[test]
fn acos_one_is_zero() {
assert_eq!(I128s12::ONE.acos(), I128s12::ZERO);
}
#[test]
fn acos_zero_is_half_pi() {
let result = I128s12::ZERO.acos();
assert!(
within_lsb(result, I128s12::half_pi(), FOUR_LSB),
"acos(0) bits {}, half_pi bits {}",
result.to_bits(),
I128s12::half_pi().to_bits(),
);
}
#[test]
fn atan_zero_is_zero() {
assert_eq!(I128s12::ZERO.atan(), I128s12::ZERO);
}
#[test]
fn asin_of_sin_round_trip() {
for raw in [
123_456_789_012_i128, -123_456_789_012_i128, 456_789_012_345_i128, -456_789_012_345_i128, 1_234_567_890_123_i128, -1_234_567_890_123_i128, ] {
let x = I128s12::from_bits(raw);
let recovered = x.sin().asin();
assert!(
within_lsb(recovered, x, FOUR_LSB),
"asin(sin(x)) != x for raw={raw}: got bits {} (delta {})",
recovered.to_bits(),
(recovered.to_bits() - x.to_bits()).abs(),
);
}
}
#[test]
fn atan2_first_quadrant_diagonal() {
let one = I128s12::ONE;
let result = one.atan2(one);
assert!(
within_lsb(result, I128s12::quarter_pi(), TWO_LSB),
"atan2(1, 1) bits {}, quarter_pi bits {}",
result.to_bits(),
I128s12::quarter_pi().to_bits(),
);
}
#[test]
fn atan2_third_quadrant_diagonal() {
let neg_one = -I128s12::ONE;
let result = neg_one.atan2(neg_one);
let three = I128s12::from_int(3);
let expected = -(I128s12::quarter_pi() * three);
assert!(
within_lsb(result, expected, TWO_LSB),
"atan2(-1, -1) bits {}, expected -3pi/4 bits {}",
result.to_bits(),
expected.to_bits(),
);
}
#[test]
fn atan2_second_quadrant_diagonal() {
let one = I128s12::ONE;
let neg_one = -I128s12::ONE;
let result = one.atan2(neg_one);
let three = I128s12::from_int(3);
let expected = I128s12::quarter_pi() * three;
assert!(
within_lsb(result, expected, TWO_LSB),
"atan2(1, -1) bits {}, expected 3pi/4 bits {}",
result.to_bits(),
expected.to_bits(),
);
}
#[test]
fn atan2_fourth_quadrant_diagonal() {
let one = I128s12::ONE;
let neg_one = -I128s12::ONE;
let result = neg_one.atan2(one);
let expected = -I128s12::quarter_pi();
assert!(
within_lsb(result, expected, TWO_LSB),
"atan2(-1, 1) bits {}, expected -pi/4 bits {}",
result.to_bits(),
expected.to_bits(),
);
}
#[test]
fn atan2_positive_x_axis_is_zero() {
let zero = I128s12::ZERO;
let one = I128s12::ONE;
assert_eq!(zero.atan2(one), I128s12::ZERO);
}
#[test]
fn sinh_zero_is_zero() {
assert_eq!(I128s12::ZERO.sinh(), I128s12::ZERO);
}
#[test]
fn cosh_zero_is_one() {
assert_eq!(I128s12::ZERO.cosh(), I128s12::ONE);
}
#[test]
fn tanh_zero_is_zero() {
assert_eq!(I128s12::ZERO.tanh(), I128s12::ZERO);
}
#[test]
fn asinh_zero_is_zero() {
assert_eq!(I128s12::ZERO.asinh(), I128s12::ZERO);
}
#[test]
fn acosh_one_is_zero() {
assert_eq!(I128s12::ONE.acosh(), I128s12::ZERO);
}
#[test]
fn atanh_zero_is_zero() {
assert_eq!(I128s12::ZERO.atanh(), I128s12::ZERO);
}
#[test]
fn cosh_squared_minus_sinh_squared_is_one() {
for raw in [
500_000_000_000_i128, -500_000_000_000_i128, 1_234_567_890_123_i128, -1_234_567_890_123_i128, 2_500_000_000_000_i128, ] {
let x = I128s12::from_bits(raw);
let ch = x.cosh();
let sh = x.sinh();
let diff = (ch * ch) - (sh * sh);
assert!(
within_lsb(diff, I128s12::ONE, FOUR_LSB),
"cosh^2 - sinh^2 != 1 for raw={raw}: got bits {} (delta {})",
diff.to_bits(),
(diff.to_bits() - I128s12::ONE.to_bits()).abs(),
);
}
}
#[test]
fn to_degrees_pi_is_180() {
let pi = I128s12::pi();
let result = pi.to_degrees();
let expected = I128s12::from_int(180);
assert!(
within_lsb(result, expected, ANGLE_TOLERANCE_LSB),
"to_degrees(pi) bits {}, expected 180 bits {} (delta {})",
result.to_bits(),
expected.to_bits(),
(result.to_bits() - expected.to_bits()).abs(),
);
}
#[test]
fn to_radians_180_is_pi() {
let one_eighty = I128s12::from_int(180);
let result = one_eighty.to_radians();
let expected = I128s12::pi();
assert!(
within_lsb(result, expected, ANGLE_TOLERANCE_LSB),
"to_radians(180) bits {}, expected pi bits {} (delta {})",
result.to_bits(),
expected.to_bits(),
(result.to_bits() - expected.to_bits()).abs(),
);
}
#[test]
fn to_degrees_zero_is_zero() {
assert_eq!(I128s12::ZERO.to_degrees(), I128s12::ZERO);
}
#[test]
fn to_radians_zero_is_zero() {
assert_eq!(I128s12::ZERO.to_radians(), I128s12::ZERO);
}
#[test]
fn to_radians_to_degrees_round_trip() {
for raw in [
500_000_000_000_i128, -500_000_000_000_i128, 1_234_567_890_123_i128, -2_345_678_901_234_i128, ] {
let x = I128s12::from_bits(raw);
let recovered = x.to_degrees().to_radians();
assert!(
within_lsb(recovered, x, FOUR_LSB),
"to_radians(to_degrees(x)) != x for raw={raw}: got bits {} (delta {})",
recovered.to_bits(),
(recovered.to_bits() - x.to_bits()).abs(),
);
}
}
#[test]
fn to_degrees_half_pi_is_90() {
let result = I128s12::half_pi().to_degrees();
let expected = I128s12::from_int(90);
assert!(
within_lsb(result, expected, ANGLE_TOLERANCE_LSB),
"to_degrees(half_pi) bits {}, expected 90 bits {} (delta {})",
result.to_bits(),
expected.to_bits(),
(result.to_bits() - expected.to_bits()).abs(),
);
}
#[test]
fn to_degrees_quarter_pi_is_45() {
let result = I128s12::quarter_pi().to_degrees();
let expected = I128s12::from_int(45);
assert!(
within_lsb(result, expected, ANGLE_TOLERANCE_LSB),
"to_degrees(quarter_pi) bits {}, expected 45 bits {} (delta {})",
result.to_bits(),
expected.to_bits(),
(result.to_bits() - expected.to_bits()).abs(),
);
}
#[test]
fn tan_matches_sin_over_cos() {
for raw in [
500_000_000_000_i128, -500_000_000_000_i128, 1_000_000_000_000_i128, -1_000_000_000_000_i128, 123_456_789_012_i128, ] {
let x = I128s12::from_bits(raw);
let t = x.tan();
let sc = x.sin() / x.cos();
assert!(
within_lsb(t, sc, FOUR_LSB),
"tan(x) != sin/cos for raw={raw}: tan bits {}, sin/cos bits {}",
t.to_bits(),
sc.to_bits(),
);
}
}
#[test]
fn tanh_matches_sinh_over_cosh() {
for raw in [
500_000_000_000_i128, -500_000_000_000_i128, 1_234_567_890_123_i128, -2_345_678_901_234_i128, ] {
let x = I128s12::from_bits(raw);
let t = x.tanh();
let sc = x.sinh() / x.cosh();
assert!(
within_lsb(t, sc, FOUR_LSB),
"tanh(x) != sinh/cosh for raw={raw}: tanh bits {}, sinh/cosh bits {}",
t.to_bits(),
sc.to_bits(),
);
}
}
}