#![cfg(feature = "float")]
use puremp::{Float, Int, Rational, RoundingMode};
const N: RoundingMode = RoundingMode::Nearest;
fn int(v: i64, prec: u64) -> Float {
Float::from_int(&Int::from_i64(v), prec, N)
}
fn rat(a: i64, b: i64, prec: u64) -> Float {
Float::from_rational(&Rational::new(Int::from_i64(a), Int::from_i64(b)), prec, N)
}
fn dec(x: &Float, digits: u32) -> String {
x.to_decimal_string(digits)
}
#[test]
fn gamma_integer_values() {
let p = 200;
assert_eq!(dec(&int(1, p).gamma(p, N), 6), "1.000000");
assert_eq!(dec(&int(2, p).gamma(p, N), 6), "1.000000");
assert_eq!(dec(&int(5, p).gamma(p, N), 6), "24.000000");
assert_eq!(dec(&int(6, p).gamma(p, N), 6), "120.000000");
let mut f = Int::ONE;
for k in 2..=19 {
f = f.mul(&Int::from_i64(k));
}
assert_eq!(
dec(&int(20, p).gamma(p, N), 2),
dec(&Float::from_int(&f, p, N), 2)
);
}
#[test]
fn gamma_half_is_sqrt_pi() {
let p = 160;
let g = rat(1, 2, p).gamma(p, N);
let sqrt_pi = Float::pi(p + 16, N).sqrt(p, N);
assert_eq!(dec(&g, 40), dec(&sqrt_pi, 40));
}
#[test]
fn gamma_three_halves_is_sqrt_pi_over_two() {
let p = 160;
let g = rat(3, 2, p).gamma(p, N);
let sqrt_pi_half = Float::pi(p + 16, N).sqrt(p, N).div(&int(2, p), p, N);
assert_eq!(dec(&g, 40), dec(&sqrt_pi_half, 40));
}
#[test]
fn gamma_negative_half_reflection() {
let p = 160;
let g = rat(-1, 2, p).gamma(p, N);
let m2_sqrt_pi = Float::pi(p + 16, N).sqrt(p, N).mul(&int(-2, p), p, N);
assert_eq!(dec(&g, 40), dec(&m2_sqrt_pi, 40));
let g = rat(-3, 2, p).gamma(p, N);
let ref_val = Float::pi(p + 16, N)
.sqrt(p, N)
.mul(&int(4, p), p, N)
.div(&int(3, p), p, N);
assert_eq!(dec(&g, 40), dec(&ref_val, 40));
}
#[test]
fn gamma_poles_are_nan() {
let p = 64;
assert!(int(0, p).gamma(p, N).is_nan());
assert!(int(-1, p).gamma(p, N).is_nan());
assert!(int(-5, p).gamma(p, N).is_nan());
assert!(Float::nan(p).gamma(p, N).is_nan());
assert!(Float::neg_infinity(p).gamma(p, N).is_nan());
let g = Float::infinity(p).gamma(p, N);
assert!(!g.is_finite() && !g.is_sign_negative());
}
#[test]
fn ln_gamma_known_values() {
let p = 200;
let lg = int(10, p).ln_gamma(p, N);
let ln_fact = int(362880, p).ln(p, N);
assert_eq!(dec(&lg, 40), dec(&ln_fact, 40));
assert_eq!(
dec(&int(5, p).ln_gamma(p, N), 32),
"3.17805383034794561964694160129706"
);
assert_eq!(
dec(&rat(1, 2, p).ln_gamma(p, N), 32),
"0.57236494292470008707171367567653"
);
assert!(int(1, p).ln_gamma(p, N).is_zero());
assert!(int(2, p).ln_gamma(p, N).is_zero());
assert!(int(-3, p).ln_gamma(p, N).is_nan());
let z = Float::zero(p).ln_gamma(p, N);
assert!(!z.is_finite() && !z.is_sign_negative());
}
#[test]
fn gamma_ziv_precision_stability() {
for &(a, b) in &[(3i64, 1i64), (7, 2), (1, 3), (5, 2), (-3, 2)] {
let x = rat(a, b, 400);
for &prec in &[53u64, 64, 100, 150] {
let direct = x.gamma(prec, N);
let high = x.gamma(prec + 64, N).round(prec, N);
assert_eq!(
dec(&direct, 40),
dec(&high, 40),
"gamma({a}/{b}) unstable at p={prec}"
);
}
}
}
#[test]
fn bessel_j_at_zero() {
let p = 100;
assert_eq!(int(0, p).bessel_j(0, p, N).to_f64(), 1.0);
assert!(int(0, p).bessel_j(1, p, N).is_zero());
assert!(int(0, p).bessel_j(5, p, N).is_zero());
assert!(int(0, p).bessel_j(-2, p, N).is_zero());
assert!(Float::nan(p).bessel_j(0, p, N).is_nan());
}
#[test]
fn bessel_j_tabulated() {
let p = 200;
assert_eq!(
dec(&int(1, p).bessel_j(0, p, N), 32),
"0.76519768655796655144971752610266"
);
assert_eq!(
dec(&int(1, p).bessel_j(1, p, N), 32),
"0.44005058574493351595968220371891"
);
assert_eq!(
dec(&int(10, p).bessel_j(0, p, N), 32),
"-0.24593576445134833519776086248533"
);
assert!((int(1, p).bessel_j(0, p, N).to_f64() - 0.7651976865579665).abs() < 1e-15);
assert!((int(1, p).bessel_j(1, p, N).to_f64() - 0.4400505857449335).abs() < 1e-15);
}
#[test]
fn bessel_j_negative_order() {
let p = 180;
for &x in &[1i64, 3, 7] {
let j1 = int(x, p).bessel_j(1, p, N);
let jm1 = int(x, p).bessel_j(-1, p, N);
assert_eq!(dec(&jm1, 45), dec(&j1.neg(), 45));
let j2 = int(x, p).bessel_j(2, p, N);
let jm2 = int(x, p).bessel_j(-2, p, N);
assert_eq!(dec(&jm2, 45), dec(&j2, 45));
let j3 = int(x, p).bessel_j(3, p, N);
let jm3 = int(x, p).bessel_j(-3, p, N);
assert_eq!(dec(&jm3, 45), dec(&j3.neg(), 45));
}
}
#[test]
fn bessel_j_ziv_precision_stability() {
for &(n, x) in &[(0i64, 1i64), (1, 2), (2, 7), (5, 3), (3, 10)] {
for &prec in &[53u64, 64, 100, 150] {
let direct = int(x, prec).bessel_j(n, prec, N);
let high = int(x, prec + 64).bessel_j(n, prec + 64, N).round(prec, N);
assert_eq!(
dec(&direct, 40),
dec(&high, 40),
"J_{n}({x}) unstable at p={prec}"
);
}
}
}
#[test]
fn bessel_i_at_zero() {
let p = 100;
assert_eq!(int(0, p).bessel_i(0, p, N).to_f64(), 1.0);
assert!(int(0, p).bessel_i(2, p, N).is_zero());
assert!(int(0, p).bessel_i(-3, p, N).is_zero());
assert!(Float::nan(p).bessel_i(0, p, N).is_nan());
}
#[test]
fn bessel_i_tabulated() {
let p = 200;
assert_eq!(
dec(&int(1, p).bessel_i(0, p, N), 32),
"1.26606587775200833559824462521472"
);
assert_eq!(
dec(&int(5, p).bessel_i(2, p, N), 32),
"17.50561496662423601488701189518042"
);
assert!((int(1, p).bessel_i(0, p, N).to_f64() - 1.2660658777520084).abs() < 1e-15);
}
#[test]
fn bessel_i_negative_order_symmetry() {
let p = 180;
for &x in &[1i64, 4] {
for n in [1i64, 2, 3] {
let pos = int(x, p).bessel_i(n, p, N);
let neg = int(x, p).bessel_i(-n, p, N);
assert_eq!(dec(&pos, 45), dec(&neg, 45));
}
}
}
#[test]
fn bessel_i_ziv_precision_stability() {
for &(n, x) in &[(0i64, 1i64), (1, 4), (2, 5), (3, 2)] {
for &prec in &[53u64, 64, 100, 150] {
let direct = int(x, prec).bessel_i(n, prec, N);
let high = int(x, prec + 64).bessel_i(n, prec + 64, N).round(prec, N);
assert_eq!(
dec(&direct, 40),
dec(&high, 40),
"I_{n}({x}) unstable at p={prec}"
);
}
}
}