use crate::common::{f_fmla, is_integer};
use crate::f_exp;
use crate::logs::simple_fast_log;
use crate::polyeval::{f_polyeval5, f_polyeval18};
use crate::rounding::CpuFloor;
use crate::sin_cosf::fast_sinpif;
pub fn f_tgammaf(x: f32) -> f32 {
let xb = x.to_bits();
if xb >= 0xffu32 << 23 || xb == 0 {
if xb.wrapping_shl(1) == 0 {
return f32::INFINITY;
}
if x.is_nan() {
return x + x;
}
if x.is_infinite() {
if x.is_sign_negative() {
return f32::NAN;
}
return x;
}
}
let x_a = f32::from_bits(x.to_bits() & 0x7fff_ffff);
if x.is_sign_positive() && x_a.to_bits() >= 0x420c2910u32 {
return f32::INFINITY;
} else if x.is_sign_negative() && x_a.to_bits() >= 0x421a67dau32 {
if x == x.cpu_floor() {
return f32::NAN;
}
return -0.0;
}
if x_a < 2e-8 {
const EULER: f64 = f64::from_bits(0x3fe2788cfc6fb619);
let dx = x as f64;
let p = 1. / dx - EULER;
return p as f32;
} else if x_a < 2e-6 {
const EULER: f64 = f64::from_bits(0x3fe2788cfc6fb619);
const A2: f64 = f64::from_bits(0x3fefa658c23b1578);
let dx = x as f64;
let p = 1. / dx + f_fmla(dx, A2, -EULER);
return p as f32;
}
let mut fact = 1.0f64;
let mut parity = 1.0;
const PI: f64 = f64::from_bits(0x400921fb54442d18);
let mut dy = x as f64;
let mut result;
if dy < 0. {
if is_integer(dy) {
return f32::NAN;
}
dy = f64::from_bits(dy.to_bits() & 0x7fff_ffff_ffff_ffff);
if x_a < f32::EPSILON {
return (1. / x as f64) as f32;
}
let y1 = x_a.cpu_floor();
let fraction = x_a - y1;
if fraction != 0.0
{
if y1 != (y1 * 0.5).cpu_floor() * 2.0 {
parity = -1.0;
}
fact = -PI / fast_sinpif(fraction);
dy += 1.0;
}
}
if dy < 12.0 {
let y1 = dy;
let z;
let mut n = 0;
if dy < 1.0 {
z = dy;
dy += 1.0;
} else
{
n = dy as i32 - 1;
dy -= n as f64;
z = dy - 1.0;
}
result = f_polyeval18(
z,
f64::from_bits(0x3fefffffffffff19),
f64::from_bits(0xbfe2788cfc6d59a6),
f64::from_bits(0x3fefa658c133abd2),
f64::from_bits(0xbfed0a116184e1d0),
f64::from_bits(0x3fef6a4cd2befb54),
f64::from_bits(0xbfef6c4479fe9aca),
f64::from_bits(0x3fefc59bfa97e9bf),
f64::from_bits(0xbfefcfde03bfb0d9),
f64::from_bits(0x3fefa3ee5eab6681),
f64::from_bits(0xbfeed8130a67dd46),
f64::from_bits(0x3fecb6e603fdb5ed),
f64::from_bits(0xbfe8801e901b4c10),
f64::from_bits(0x3fe2356219082d3e),
f64::from_bits(0xbfd64ad556b6062a),
f64::from_bits(0x3fc5219e235288f4),
f64::from_bits(0xbfacad444cb0f4ec),
f64::from_bits(0x3f888ea3bfa9155a),
f64::from_bits(0xbf53d1776abc9eaa),
);
if y1 < dy {
result /= y1;
} else if y1 > dy {
for _ in 0..n {
result *= dy;
dy += 1.0;
}
}
} else {
let y_recip = 1. / dy;
let y_sqr = y_recip * y_recip;
let bernoulli_poly = f_polyeval5(
y_sqr,
f64::from_bits(0x3fb5555555555555),
f64::from_bits(0xbf66c16c16c16c17),
f64::from_bits(0x3f4a01a01a01a01a),
f64::from_bits(0xbf43813813813814),
f64::from_bits(0x3f4b951e2b18ff23),
);
const LOG2_PI_OVER_2: f64 = f64::from_bits(0x3fed67f1c864beb5);
let mut log_gamma = f_fmla(bernoulli_poly, y_recip, -dy) + LOG2_PI_OVER_2;
log_gamma = f_fmla(simple_fast_log(dy), dy - 0.5, log_gamma);
result = f_exp(log_gamma);
}
result *= parity;
if fact != 1.0 {
result = fact / result;
}
result as f32
}
#[cfg(test)]
mod tests {
use crate::f_tgammaf;
#[test]
fn test_gammaf() {
assert!(f_tgammaf(-3.).is_nan());
assert_eq!(f_tgammaf(12.58038769), 167149000.0);
assert_eq!(f_tgammaf(4.566854e-7), 2189690.8);
assert_eq!(f_tgammaf(4.9587783e-8), 20166258.0);
assert_eq!(f_tgammaf(-46.1837), -0.0);
assert_eq!(f_tgammaf(1.1502532e-19), 8.6937384e18);
assert_eq!(f_tgammaf(0.04038769), 24.221333);
assert_eq!(f_tgammaf(2.58038769), 1.4088479);
assert_eq!(f_tgammaf(-1.3559477), 2.892815);
assert_eq!(f_tgammaf(-2.3559477), -1.2278775);
assert_eq!(f_tgammaf(0.0), f32::INFINITY);
assert_eq!(f_tgammaf(-0.0), f32::INFINITY);
assert_eq!(f_tgammaf(f32::INFINITY), f32::INFINITY);
assert!(f_tgammaf(f32::NEG_INFINITY).is_nan());
assert!(f_tgammaf(f32::NAN).is_nan());
}
}