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_self_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();
let ulp = r_p.ulp();
prop_assert!((r_p - r_2p).abs() <= ulp);
}
}