use proptest::prelude::*;
use proptest::test_runner::Config;
#[test]
fn lerp_boundary() {
let config = Config::with_cases(100);
proptest!(config, |(a in -1000i32..1000i32, b in -1000i32..1000i32)| {
let a = a as f64;
let b = b as f64;
let result_0 = mathverse_core::ops::lerp(a, b, 0.0);
let result_1 = mathverse_core::ops::lerp(a, b, 1.0);
prop_assert!((result_0 - a).abs() < 1e-10, "lerp(a,b,0) should equal a");
prop_assert!((result_1 - b).abs() < 1e-10, "lerp(a,b,1) should equal b");
});
}
#[test]
fn smoothstep_bounds() {
let config = Config::with_cases(100);
proptest!(config, |(x in 0.0f64..=1.0)| {
let s = mathverse_core::ops::smoothstep(x);
prop_assert!((0.0..=1.0).contains(&s), "smoothstep should be in [0,1], got {s}");
});
}
#[test]
fn smoothstep_endpoints() {
let s0 = mathverse_core::ops::smoothstep(0.0_f64);
let s1 = mathverse_core::ops::smoothstep(1.0_f64);
assert!(s0.abs() < 1e-12, "smoothstep(0) should be 0");
assert!((s1 - 1.0).abs() < 1e-12, "smoothstep(1) should be 1");
}
#[test]
fn gcd_symmetry() {
let config = Config::with_cases(100);
proptest!(config, |(a in 1u64..10000u64, b in 1u64..10000u64)| {
prop_assert_eq!(mathverse_core::algorithms::gcd(a, b), mathverse_core::algorithms::gcd(b, a));
});
}
#[test]
fn gcd_divides() {
let config = Config::with_cases(100);
proptest!(config, |(a in 1u64..1000u64, b in 1u64..1000u64)| {
let g = mathverse_core::algorithms::gcd(a, b);
prop_assert_eq!(a % g, 0, "gcd should divide a");
prop_assert_eq!(b % g, 0, "gcd should divide b");
});
}
#[test]
fn lcm_identity() {
let config = Config::with_cases(100);
proptest!(config, |(a in 1u64..100u64, b in 1u64..100u64)| {
let l = mathverse_core::algorithms::lcm(a, b);
let g = mathverse_core::algorithms::gcd(a, b);
prop_assert_eq!(l * g, a * b);
});
}
#[test]
fn is_prime_matches_sieve() {
let config = Config::with_cases(100);
proptest!(config, |(n in 2u64..200u64)| {
let prime = mathverse_core::algorithms::is_prime(n);
let sieve = mathverse_core::algorithms::sieve_of_eratosthenes(n as usize).contains(&n);
prop_assert_eq!(prime, sieve);
});
}
#[test]
fn factorial_positive() {
let config = Config::with_cases(100);
proptest!(config, |(n in 0u64..21u64)| {
let f = mathverse_core::algorithms::factorial(n);
prop_assert!(f > 0, "factorial should be positive for n >= 0");
if n >= 2u64 {
let f_prev = mathverse_core::algorithms::factorial(n - 1);
prop_assert_eq!(f, f_prev * n as u128, "n! = (n-1)! * n");
}
});
}
#[test]
fn binomial_symmetry() {
let config = Config::with_cases(100);
proptest!(config, |(n in 0u64..30u64, k in 0u64..30u64)| {
if k <= n {
let b1 = mathverse_core::algorithms::binomial(n, k);
let b2 = mathverse_core::algorithms::binomial(n, n - k);
prop_assert_eq!(b1, b2, "C(n,k) should equal C(n,n-k)");
}
});
}
#[test]
fn wrap_in_range() {
let config = Config::with_cases(100);
proptest!(config, |(x in -1000.0f64..1000.0f64, lo in 0.0f64..10.0f64, hi in 10.0f64..20.0f64)| {
let w = mathverse_core::ops::wrap(x, lo, hi);
prop_assert!(w >= lo && w < hi, "wrap should be in [lo, hi), got {w}");
});
}
#[test]
fn abs_always_nonneg() {
let config = Config::with_cases(100);
proptest!(config, |(x in -1000.0f64..1000.0f64)| {
let a = x.abs();
prop_assert!(a >= 0.0, "abs should be non-negative");
});
}
#[test]
fn hypot2_pythagorean() {
let config = Config::with_cases(100);
proptest!(config, |(a in 0.0f64..100.0f64, b in 0.0f64..100.0f64)| {
let h = mathverse_core::ops::hypot2(a, b);
let expected = (a * a + b * b).sqrt();
prop_assert!((h - expected).abs() < 1e-10, "hypot2 should match sqrt(a^2+b^2)");
});
}
#[test]
fn div_by_gcd_coprime() {
let config = Config::with_cases(100);
proptest!(config, |(a in 1u64..1000u64, b in 1u64..1000u64)| {
let g = mathverse_core::algorithms::gcd(a, b);
let a_red = a / g;
let b_red = b / g;
prop_assert!(mathverse_core::algorithms::is_coprime(a_red, b_red),
"a/gcd and b/gcd should be coprime");
});
}
#[test]
fn digit_sum_bounded() {
let config = Config::with_cases(100);
proptest!(config, |(n in 0u64..1000000u64)| {
let s = mathverse_core::algorithms::digit_sum(n);
let dc = mathverse_core::algorithms::digit_count(n);
prop_assert!(s <= 9 * dc as u64, "digit_sum should be bounded by 9 * digit_count");
});
}
#[test]
fn fibonacci_growth() {
let config = Config::with_cases(100);
proptest!(config, |(n in 2u64..50u64)| {
let f_n = mathverse_core::algorithms::fibonacci(n);
let f_prev = mathverse_core::algorithms::fibonacci(n - 1);
prop_assert!(f_n >= f_prev, "fibonacci should be non-decreasing");
});
}
#[test]
fn euler_phi_upper_bound() {
let config = Config::with_cases(100);
proptest!(config, |(n in 1u64..200u64)| {
let phi = mathverse_core::algorithms::euler_phi(n);
prop_assert!(phi <= n, "phi(n) should be <= n");
prop_assert!(phi >= 1, "phi(n) should be >= 1");
});
}
#[test]
fn is_palindrome_reverse() {
let config = Config::with_cases(100);
proptest!(config, |(n in 0u64..100000u64)| {
let p = mathverse_core::algorithms::is_palindrome(n);
let rev = mathverse_core::algorithms::reverse_digits(n);
prop_assert_eq!(p, n == rev, "is_palindrome should match n == reverse_digits(n)");
});
}
#[test]
fn trig_pythagorean_identity() {
let config = Config::with_cases(100);
proptest!(config, |(x in -10.0f64..10.0f64)| {
let s = x.sin();
let c = x.cos();
let val = s * s + c * c;
prop_assert!((val - 1.0).abs() < 1e-10, "sin²+cos² should be 1, got {val}");
});
}
#[test]
fn trig_tan_is_sin_over_cos() {
let config = Config::with_cases(100);
proptest!(config, |(x in -1.5f64..1.5f64)| {
let t = x.tan();
let sc = x.sin() / x.cos();
prop_assert!((t - sc).abs() < 1e-10, "tan should equal sin/cos");
});
}
#[test]
fn trig_periodicity() {
let config = Config::with_cases(100);
proptest!(config, |(x in -100.0f64..100.0f64)| {
let two_pi = 2.0 * core::f64::consts::PI;
prop_assert!((x.sin() - (x + two_pi).sin()).abs() < 1e-10, "sin should be 2Ï€-periodic");
prop_assert!((x.cos() - (x + two_pi).cos()).abs() < 1e-10, "cos should be 2Ï€-periodic");
});
}
#[test]
fn trig_inverse_roundtrip() {
let config = Config::with_cases(100);
proptest!(config, |(x in -1.0f64..1.0f64)| {
prop_assert!((x.asin().sin() - x).abs() < 1e-10, "asin(sin(x)) should roundtrip");
prop_assert!((x.acos().cos() - x).abs() < 1e-10, "acos(cos(x)) should roundtrip");
});
}
#[test]
fn hyperbolic_identity() {
let config = Config::with_cases(100);
proptest!(config, |(x in -5.0f64..5.0f64)| {
let c = x.cosh();
let s = x.sinh();
let val = c * c - s * s;
prop_assert!((val - 1.0).abs() < 1e-10, "cosh²-sinh² should be 1, got {val}");
});
}
#[test]
fn hyperbolic_tanh_bounded() {
let config = Config::with_cases(100);
proptest!(config, |(x in -100.0f64..100.0f64)| {
let t = x.tanh();
prop_assert!(t >= -1.0 && t <= 1.0, "tanh should be in [-1,1], got {t}");
});
}
#[test]
fn hyperbolic_inverse_roundtrip() {
let config = Config::with_cases(100);
proptest!(config, |(x in -5.0f64..5.0f64)| {
prop_assert!((x.asinh().sinh() - x).abs() < 1e-10, "asinh(sinh(x)) roundtrip");
});
proptest!(config, |(x in 1.0f64..5.0f64)| {
prop_assert!((x.acosh().cosh() - x).abs() < 1e-10, "acosh(cosh(x)) roundtrip");
});
}
#[test]
fn transcendental_exp_ln_roundtrip() {
let config = Config::with_cases(100);
proptest!(config, |(x in 0.1f64..100.0f64)| {
prop_assert!((x.ln().exp() - x).abs() < 1e-10, "ln(exp(x)) roundtrip");
prop_assert!((x.exp().ln() - x).abs() < 1e-10, "exp(ln(x)) roundtrip");
});
}
#[test]
fn transcendental_log_consistency() {
let config = Config::with_cases(100);
proptest!(config, |(x in 0.1f64..100.0f64)| {
let l10 = x.log10();
let l2 = x.log2();
let ln = x.ln();
let two = 2.0_f64;
let ten = 10.0_f64;
prop_assert!((l10 - ln / ten.ln()).abs() < 1e-10, "log10 should equal ln/ln(10)");
prop_assert!((l2 - ln / two.ln()).abs() < 1e-10, "log2 should equal ln/ln(2)");
});
}
#[test]
fn float_class_consistency() {
let config = Config::with_cases(100);
proptest!(config, |(x in -1000.0f64..1000.0f64)| {
prop_assert!(x.is_finite(), "normal finite numbers should be is_finite");
prop_assert!(!x.is_nan(), "normal numbers should not be NaN");
prop_assert!(!x.is_infinite(), "normal numbers should not be infinite");
});
proptest!(config, |(x in 0.1f64..1000.0f64)| {
prop_assert!(x.is_normal(), "non-zero finite should be is_normal");
});
}