use crate::consts::*;
pub fn gammaln(x: f64) -> f64 {
match x {
_ if x <= 0.0 || x.is_nan() => f64::NAN,
_ if x.is_infinite() => f64::INFINITY,
_ if x < 0.00001 => {
-f64::ln(x) + x * (-EULER_MASCHERONI + x * RIEMANN_ZETA[2] / 2.0)
}
1.0 | 2.0 => 0.0,
_ => {
let mut current_x = x;
let mut correction = 1.0;
while current_x < 10.0 {
correction = correction / current_x;
current_x = current_x + 1.0;
}
let inv_x_sq = 1.0 / (current_x * current_x);
LN_2PI * 0.5 + f64::ln(correction) + f64::ln(current_x) * (current_x - 0.5) - current_x
+ ((((((inv_x_sq * STIRLING_ASYMPTOTIC_SERIES[6]
+ STIRLING_ASYMPTOTIC_SERIES[5])
* inv_x_sq
+ STIRLING_ASYMPTOTIC_SERIES[4])
* inv_x_sq
+ STIRLING_ASYMPTOTIC_SERIES[3])
* inv_x_sq
+ STIRLING_ASYMPTOTIC_SERIES[2])
* inv_x_sq
+ STIRLING_ASYMPTOTIC_SERIES[1])
* inv_x_sq
+ STIRLING_ASYMPTOTIC_SERIES[0])
/ current_x
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn assert_approx_eq(actual: f64, expected: f64, epsilon: f64) {
if actual.is_nan() && expected.is_nan() {
return;
}
if actual.is_infinite() && expected.is_infinite() {
assert_eq!(actual.is_sign_positive(), expected.is_sign_positive());
return;
}
let diff = (actual - expected).abs();
assert!(
diff < epsilon,
"Assertion failed: actual {} != expected {} (diff {} > epsilon {})",
actual,
expected,
diff,
epsilon
);
}
#[test]
fn test_gammaln_special_cases() {
assert!(gammaln(f64::NAN).is_nan());
assert!(gammaln(0.0).is_nan());
assert!(gammaln(-1.0).is_nan());
assert_eq!(gammaln(f64::INFINITY), f64::INFINITY);
}
#[test]
fn test_gammaln_small_values() {
assert_approx_eq(gammaln(1e-7), 16.11809559323676, 1e-10);
}
#[test]
fn test_gammaln_integers() {
assert_eq!(gammaln(1.0), 0.0);
assert_eq!(gammaln(2.0), 0.0);
assert_approx_eq(gammaln(3.0), 0.6931471805599453, 1e-14);
assert_approx_eq(gammaln(10.0), 12.801827480081469, 1e-14);
}
#[test]
fn test_gammaln_half_integers() {
assert_approx_eq(gammaln(0.5), 0.5723649429247001, 1e-14);
assert_approx_eq(gammaln(1.5), -0.12078223763524522, 1e-14);
}
#[test]
fn test_gammaln_extreme_values() {
let x_tiny = 1e-300;
assert_approx_eq(gammaln(x_tiny), -f64::ln(x_tiny), 1e-12);
assert_approx_eq(gammaln(1e100), 2.2925850929940457e102, 1e88);
}
}