pub fn gamma(x: f64) -> f64 {
if x.is_nan() { return f64::NAN; }
if x.is_infinite() {
return if x.is_sign_positive() { f64::INFINITY } else { f64::NAN };
}
if x <= 0.0 && x == x.floor() {
return f64::INFINITY;
}
if x > 0.0 && x == x.floor() && x <= 23.0 {
let factorial: [f64; 23] = [
1.0, 1.0, 2.0, 6.0, 24.0, 120.0, 720.0, 5040.0, 40320.0,
362880.0, 3628800.0, 39916800.0, 479001600.0, 6227020800.0,
87178291200.0, 1307674368000.0, 20922789888000.0, 355687428096000.0,
6402373705728000.0, 121645100408832000.0, 2432902008176640000.0,
51090942171709440000.0, 1124000727777607680000.0
];
return factorial[(x - 1.0) as usize];
}
if x < 0.5 {
return std::f64::consts::PI / ((std::f64::consts::PI * x).sin() * gamma(1.0 - x));
}
const G: f64 = 7.0;
const P: [f64; 9] = [
0.99999999999980993,
676.5203681218851,
-1259.1392167224028,
771.32342877765313,
-176.61502916214059,
12.507343278686905,
-0.13857109526572012,
9.9843695780195716e-6,
1.5056327351493116e-7,
];
let z = x - 1.0;
let mut a = P[0];
for i in 1..9 {
a += P[i] / (z + i as f64);
}
let t = z + G + 0.5;
const SQRT_2PI: f64 = 2.5066282746310005;
SQRT_2PI * t.powf(z + 0.5) * (-t).exp() * a
}
#[cfg(test)]
mod tests {
use super::*;
const EPSILON: f64 = 1e-14;
#[test]
fn test_exact_integers() {
assert_eq!(gamma(1.0), 1.0);
assert_eq!(gamma(2.0), 1.0);
assert_eq!(gamma(3.0), 2.0);
assert_eq!(gamma(4.0), 6.0);
assert_eq!(gamma(5.0), 24.0);
assert_eq!(gamma(10.0), 362880.0);
}
#[test]
fn test_half_integers() {
let sqrt_pi = std::f64::consts::PI.sqrt();
assert!((gamma(0.5) - sqrt_pi).abs() < EPSILON);
assert!((gamma(1.5) - 0.5 * sqrt_pi).abs() < EPSILON);
assert!((gamma(2.5) - 0.75 * sqrt_pi).abs() < EPSILON);
}
#[test]
fn test_recurrence_relation() {
let x = 3.14159;
let lhs = gamma(x + 1.0);
let rhs = x * gamma(x);
assert!((lhs - rhs).abs() / lhs < EPSILON);
}
#[test]
fn test_negative_values() {
let expected = -2.0 * std::f64::consts::PI.sqrt();
assert!((gamma(-0.5) - expected).abs() < EPSILON);
}
#[test]
fn test_special_cases() {
assert!(gamma(0.0).is_infinite());
assert!(gamma(-1.0).is_infinite());
assert!(gamma(f64::NAN).is_nan());
assert!(gamma(f64::INFINITY).is_infinite());
}
}