use dashu_float::ops::Abs;
use dashu_float::DBig;
use dashu_int::IBig;
use proptest::prelude::*;
const P: usize = 50;
fn tol(slack: isize) -> DBig {
DBig::from_parts(IBig::from(1), -(P as isize) + slack)
}
fn x_in(min: i64, max: i64) -> impl Strategy<Value = DBig> {
(min..=max).prop_map(|m| {
DBig::from_parts(IBig::from(m), -4)
.with_precision(P)
.value()
})
}
fn one() -> DBig {
DBig::ONE.with_precision(P).value()
}
proptest! {
#![proptest_config(ProptestConfig { cases: 64, ..Default::default() })]
#[test]
fn pythagorean(x in x_in(-200_000, 200_000)) {
for p in [20usize, 50, 100] {
let xp = x.clone().with_precision(p).value();
let (s, c) = xp.sin_cos();
let one = DBig::ONE.with_precision(p).value();
let resid = (&s * &s + &c * &c - &one).abs();
let tol = DBig::from_parts(IBig::from(1), -(p as isize) + 2);
prop_assert!(resid < tol, "sin^2+cos^2 != 1 at prec {p}, x = {xp}");
}
}
#[test]
fn double_angle(x in x_in(-200_000, 200_000)) {
let (s, c) = x.sin_cos();
let two_x = &x + &x;
let (s2, c2) = two_x.sin_cos();
let two_sc = (&s * &c) + (&s * &c);
prop_assert!((s2 - two_sc).abs() < tol(2));
let cos2x = (&c * &c) - (&s * &s);
prop_assert!((c2 - cos2x).abs() < tol(2));
}
#[test]
fn atan_reciprocal(x in x_in(1, 200_000)) {
let half_pi = DBig::pi(P) / 2i32;
let inv = one() / &x;
let resid = (x.atan() + inv.atan()) - half_pi;
prop_assert!(resid.abs() < tol(2));
}
#[test]
fn parity(x in x_in(-200_000, 200_000)) {
let s = x.sin();
let c = x.cos();
let nx = -x;
prop_assert!((nx.sin() + s).abs() < tol(2));
prop_assert!((nx.cos() - c).abs() < tol(2));
}
#[test]
fn sin_cos_consistency(x in x_in(-200_000, 200_000)) {
let (s, c) = x.sin_cos();
prop_assert!((s - x.sin()).abs() < tol(2));
prop_assert!((c - x.cos()).abs() < tol(2));
}
}