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_from(m: i64) -> DBig {
DBig::from_parts(IBig::from(m), -4)
.with_precision(P)
.value()
}
fn pos_x(max_m: i64) -> impl Strategy<Value = DBig> {
(1..=max_m).prop_map(x_from)
}
fn signed_x(max_m: i64) -> impl Strategy<Value = DBig> {
(-max_m..=max_m)
.prop_map(x_from)
.prop_filter("nonzero", |x| *x != DBig::ZERO)
}
proptest! {
#![proptest_config(ProptestConfig { cases: 64, ..Default::default() })]
#[test]
fn exp_ln_inverse(x in pos_x(100_000)) {
let y = x.ln().exp();
prop_assert!((y - x).abs() < tol(4));
}
#[test]
fn ln_exp_inverse(x in signed_x(2_000)) {
let y = x.exp().ln();
prop_assert!((y - x).abs() < tol(4));
}
#[test]
fn ln_product(x in pos_x(100_000), y in pos_x(100_000)) {
let lhs = (x.clone() * y.clone()).ln();
let rhs = x.ln() + y.ln();
prop_assert!((lhs - rhs).abs() < tol(3));
}
#[test]
fn powf_identity(x in pos_x(50_000)) {
let one = DBig::ONE.with_precision(P).value();
let y = x.powf(&one);
prop_assert!((y - x).abs() < tol(2));
}
#[test]
fn nth_root_within_ulp(x in pos_x(1_000_000), n in 2usize..=7) {
let r = x.nth_root(n);
let ulp = r.ulp();
let r = r.with_precision(0).value();
let ulp = ulp.with_precision(0).value();
let x = x.with_precision(0).value();
let lower = (r.clone() - ulp.clone()).powi(IBig::from(n));
let upper = (r.clone() + ulp).powi(IBig::from(n));
prop_assert!(lower < x && x < upper);
}
#[test]
fn ln_rounding_exact_oracle(x in pos_x(100_000)) {
let r_p = x.ln();
let x_2p = x.clone().with_precision(2 * P).value();
let r_2p = x_2p.ln().with_precision(P).value();
prop_assert_eq!(r_p, r_2p);
}
#[test]
fn exp_rounding_exact_oracle(x in signed_x(2_000)) {
let r_p = x.exp();
let x_2p = x.clone().with_precision(2 * P).value();
let r_2p = x_2p.exp().with_precision(P).value();
prop_assert_eq!(r_p, r_2p);
}
#[test]
fn exp_m1_rounding_exact_oracle(x in signed_x(2_000)) {
let r_p = x.exp_m1();
let x_2p = x.clone().with_precision(2 * P).value();
let r_2p = x_2p.exp_m1().with_precision(P).value();
prop_assert_eq!(r_p, r_2p);
}
#[test]
fn ln_1p_rounding_exact_oracle(m in -9999i64..100_000i64) {
let x = x_from(m);
let r_p = x.ln_1p();
let x_2p = x.clone().with_precision(2 * P).value();
let r_2p = x_2p.ln_1p().with_precision(P).value();
prop_assert_eq!(r_p, r_2p);
}
#[test]
fn hypot_rounding_exact_oracle(a in signed_x(100_000), b in signed_x(100_000)) {
let r_p = a.clone().hypot(&b);
let a_2p = a.clone().with_precision(2 * P).value();
let b_2p = b.clone().with_precision(2 * P).value();
let r_2p = a_2p.hypot(&b_2p).with_precision(P).value();
prop_assert_eq!(r_p, r_2p);
}
#[test]
fn sinh_rounding_exact_oracle(x in signed_x(2_000)) {
let r_p = x.sinh();
let x_2p = x.clone().with_precision(2 * P).value();
let r_2p = x_2p.sinh().with_precision(P).value();
prop_assert_eq!(r_p, r_2p);
}
#[test]
fn cosh_rounding_exact_oracle(x in signed_x(2_000)) {
let r_p = x.cosh();
let x_2p = x.clone().with_precision(2 * P).value();
let r_2p = x_2p.cosh().with_precision(P).value();
prop_assert_eq!(r_p, r_2p);
}
#[test]
fn tanh_rounding_exact_oracle(x in signed_x(200_000)) {
let r_p = x.tanh();
let x_2p = x.clone().with_precision(2 * P).value();
let r_2p = x_2p.tanh().with_precision(P).value();
prop_assert_eq!(r_p, r_2p);
}
#[test]
fn sinh_cosh_rounding_exact_oracle(x in signed_x(2_000)) {
let (s_p, c_p) = x.sinh_cosh();
let x_2p = x.clone().with_precision(2 * P).value();
let (s_2p, c_2p) = x_2p.sinh_cosh();
prop_assert_eq!(s_p, s_2p.with_precision(P).value());
prop_assert_eq!(c_p, c_2p.with_precision(P).value());
}
#[test]
fn asinh_rounding_exact_oracle(x in signed_x(200_000)) {
let r_p = x.asinh();
let x_2p = x.clone().with_precision(2 * P).value();
let r_2p = x_2p.asinh().with_precision(P).value();
prop_assert_eq!(r_p, r_2p);
}
#[test]
fn acosh_rounding_exact_oracle(m in 10001i64..200_000i64) {
let x = x_from(m);
let r_p = x.acosh();
let x_2p = x.clone().with_precision(2 * P).value();
let r_2p = x_2p.acosh().with_precision(P).value();
prop_assert_eq!(r_p, r_2p);
}
#[test]
fn atanh_rounding_exact_oracle(m in -9999i64..9999i64) {
let x = x_from(m);
let r_p = x.atanh();
let x_2p = x.clone().with_precision(2 * P).value();
let r_2p = x_2p.atanh().with_precision(P).value();
prop_assert_eq!(r_p, r_2p);
}
#[test]
fn sin_rounding_exact_oracle(x in signed_x(200_000)) {
let r_p = x.sin();
let x_2p = x.clone().with_precision(2 * P).value();
let r_2p = x_2p.sin().with_precision(P).value();
prop_assert_eq!(r_p, r_2p);
}
#[test]
fn cos_rounding_exact_oracle(x in signed_x(200_000)) {
let r_p = x.cos();
let x_2p = x.clone().with_precision(2 * P).value();
let r_2p = x_2p.cos().with_precision(P).value();
prop_assert_eq!(r_p, r_2p);
}
#[test]
fn sin_cos_rounding_exact_oracle(x in signed_x(200_000)) {
let (s_p, c_p) = x.sin_cos();
let x_2p = x.clone().with_precision(2 * P).value();
let (s_2p, c_2p) = x_2p.sin_cos();
prop_assert_eq!(s_p, s_2p.with_precision(P).value());
prop_assert_eq!(c_p, c_2p.with_precision(P).value());
}
#[test]
fn tan_rounding_exact_oracle(x in signed_x(200_000)) {
let r_p = x.tan();
let x_2p = x.clone().with_precision(2 * P).value();
let r_2p = x_2p.tan().with_precision(P).value();
prop_assert_eq!(r_p, r_2p);
}
#[test]
fn atan_rounding_exact_oracle(x in signed_x(200_000)) {
let r_p = x.atan();
let x_2p = x.clone().with_precision(2 * P).value();
let r_2p = x_2p.atan().with_precision(P).value();
prop_assert_eq!(r_p, r_2p);
}
#[test]
fn asin_rounding_exact_oracle(m in -10000i64..10000i64) {
let x = x_from(m);
let r_p = x.asin();
let x_2p = x.clone().with_precision(2 * P).value();
let r_2p = x_2p.asin().with_precision(P).value();
prop_assert_eq!(r_p, r_2p);
}
#[test]
fn acos_rounding_exact_oracle(m in -10000i64..10000i64) {
let x = x_from(m);
let r_p = x.acos();
let x_2p = x.clone().with_precision(2 * P).value();
let r_2p = x_2p.acos().with_precision(P).value();
prop_assert_eq!(r_p, r_2p);
}
#[test]
fn atan2_rounding_exact_oracle(y in signed_x(200_000), x in signed_x(200_000)) {
let r_p = y.clone().atan2(&x);
let y_2p = y.clone().with_precision(2 * P).value();
let x_2p = x.clone().with_precision(2 * P).value();
let r_2p = y_2p.atan2(&x_2p).with_precision(P).value();
prop_assert_eq!(r_p, r_2p);
}
#[test]
fn powf_rounding_exact_oracle(b in pos_x(100_000), m in -2_000_000i64..=2_000_000i64) {
prop_assume!(m != 0 && m % 10000 != 0);
let e = x_from(m);
let r_p = b.clone().powf(&e);
let b_2p = b.clone().with_precision(2 * P).value();
let e_2p = e.clone().with_precision(2 * P).value();
let r_2p = b_2p.powf(&e_2p).with_precision(P).value();
prop_assert_eq!(r_p, r_2p);
}
#[test]
fn powi_rounding_exact_oracle(b in signed_x(50_000), n in -12i32..=12) {
prop_assume!(n != 0);
let r_p = b.powi(IBig::from(n));
let b_2p = b.clone().with_precision(2 * P).value();
let r_2p = b_2p.powi(IBig::from(n)).with_precision(P).value();
prop_assert_eq!(r_p, r_2p);
}
}