use crate::common::{f_fmla, is_integerf, is_odd_integerf};
use crate::logs::simple_fast_log;
use crate::polyeval::{f_estrin_polyeval4, f_polyeval5, f_polyeval7, f_polyeval8};
use crate::rounding::CpuFloor;
use crate::sin_cosf::fast_sinpif;
#[inline]
pub(crate) fn lgamma_coref(x: f32) -> (f64, i32) {
let ax = f32::from_bits(x.to_bits() & 0x7fff_ffff);
let dx = ax as f64;
let is_positive = x.is_sign_positive();
let mut sum_parity = 1f64;
let mut f_res = 0f64;
let mut signgam: i32 = 1i32;
if !is_positive {
let y1 = ax.cpu_floor();
let fraction = ax - y1;
let a = fast_sinpif(fraction);
sum_parity = -1.;
const LOG_PI: f64 = f64::from_bits(0x3ff250d048e7a1bd);
f_res = LOG_PI - simple_fast_log(a * dx);
let is_odd = (!is_odd_integerf(y1)) as i32;
signgam = 1 - (is_odd << 1);
}
if ax <= 0.007 {
let num = f_estrin_polyeval4(
dx,
f64::from_bits(0xbb49bbc47e595350),
f64::from_bits(0xbfe2788cfc6fb2d3),
f64::from_bits(0x3fc829299b97c525),
f64::from_bits(0x3fd8e2e8aab1247c),
);
let den = f_estrin_polyeval4(
dx,
f64::from_bits(0x3ff0000000000000),
f64::from_bits(0x3ff190e5bf5a8162),
f64::from_bits(0x3fc927632366c502),
f64::from_bits(0xbf8b4ea7bd1b808e),
);
f_res = f_fmla(num / den - simple_fast_log(dx), sum_parity, f_res);
} else if ax < 1. {
let num = f_polyeval7(
dx,
f64::from_bits(0xbd24e4cf78c8818a),
f64::from_bits(0xbfe2788cfc6f64e6),
f64::from_bits(0xbfe1ea632fe853b2),
f64::from_bits(0x3fd988d2daad3806),
f64::from_bits(0x3fe1f4870eaafdf4),
f64::from_bits(0x3fc51e12d5b97330),
f64::from_bits(0x3f889ebeb9f0c8ff),
);
let den = f_polyeval7(
dx,
f64::from_bits(0x3ff0000000000000),
f64::from_bits(0x4003289872066f33),
f64::from_bits(0x4000373d88c32fe5),
f64::from_bits(0x3fe71e95bc874164),
f64::from_bits(0x3fb9936a0e008be7),
f64::from_bits(0x3f6cba20a1988a60),
f64::from_bits(0xbef3ca884ba4237a),
);
f_res = f_fmla(num / den - simple_fast_log(dx), sum_parity, f_res);
} else if ax <= 4. {
let num = f_polyeval8(
dx,
f64::from_bits(0xbe630847302a205f),
f64::from_bits(0xbfe2788c24a7deff),
f64::from_bits(0xbfdd0f61b8171907),
f64::from_bits(0x3fdab4ec27801e42),
f64::from_bits(0x3fde11fd49427a3c),
f64::from_bits(0x3fc0dbce39c660da),
f64::from_bits(0x3f88e0b4cd3e97f7),
f64::from_bits(0x3f328fcd9e9ee94e),
);
let den = f_polyeval8(
dx,
f64::from_bits(0x3ff0000000000000),
f64::from_bits(0x4001b135a31bffae),
f64::from_bits(0x3ffbbecb5e39f4ea),
f64::from_bits(0x3fe2e4da095d10bd),
f64::from_bits(0x3fb63cb7548d0d30),
f64::from_bits(0x3f73a67613163399),
f64::from_bits(0x3f10edd4e2b80e6f),
f64::from_bits(0xbe77f4e4068f9d32),
);
f_res = f_fmla(num / den - simple_fast_log(dx), sum_parity, f_res);
} else if ax <= 12. {
let num = f_polyeval8(
dx,
f64::from_bits(0x40086069fcc21690),
f64::from_bits(0x4008c8ef8602e78d),
f64::from_bits(0xc0186323149757ae),
f64::from_bits(0xbff6a9ded42d3f31),
f64::from_bits(0x3ff1adb5566cd807),
f64::from_bits(0x3fcff023390dead6),
f64::from_bits(0x3f8977a5454ec393),
f64::from_bits(0x3f21361088f694dc),
);
let den = f_polyeval8(
dx,
f64::from_bits(0x3ff0000000000000),
f64::from_bits(0x4016096b397dbe73),
f64::from_bits(0x401478926593a395),
f64::from_bits(0x3ff6a931f9e31511),
f64::from_bits(0x3fc0fbe57bf263a4),
f64::from_bits(0x3f7023f51ba39a98),
f64::from_bits(0x3efabd75ebfbde40),
f64::from_bits(0xbe4b2c9e2e4e7daa),
);
f_res = f_fmla(num / den, sum_parity, f_res);
} else {
let y_recip = 1. / dx;
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, -dx) + LOG2_PI_OVER_2;
log_gamma = f_fmla(simple_fast_log(dx), dx - 0.5, log_gamma);
f_res = f_fmla(log_gamma, sum_parity, f_res);
}
(f_res, signgam)
}
pub fn f_lgamma_rf(x: f32) -> (f32, i32) {
let xb = x.to_bits().wrapping_shl(1);
if xb >= 0xffu32 << 24 || xb == 0 {
if x.is_infinite() {
return (f32::INFINITY, 1);
}
if x.is_nan() {
return (f32::NAN, 1);
}
if xb == 0 {
return (f32::INFINITY, 1 - 2 * (x.to_bits() >> 31) as i32);
}
}
if is_integerf(x) {
if x == 2. || x == 1. {
return (0., 1);
}
if x.is_sign_negative() {
let is_odd = (!is_odd_integerf(x)) as i32;
return (f32::INFINITY, 1 - (is_odd << 1));
}
}
let c_gamma = lgamma_coref(x);
let fx = c_gamma.0 as f32;
(fx, c_gamma.1)
}
#[cfg(test)]
mod tests {
use crate::f_lgamma_rf;
#[test]
fn test_lgamma_rf() {
assert_eq!(f_lgamma_rf(f32::NEG_INFINITY), (f32::INFINITY, 1));
assert_eq!(f_lgamma_rf(f32::INFINITY), (f32::INFINITY, 1));
assert_eq!(f_lgamma_rf(0.), (f32::INFINITY, 1));
assert_eq!(f_lgamma_rf(-0.), (f32::INFINITY, -1));
assert_eq!(f_lgamma_rf(5.), (3.1780539, 1));
assert_eq!(f_lgamma_rf(-4.5), (-2.8130841, -1));
assert_eq!(f_lgamma_rf(-2.0015738), (5.7596655, -1));
}
}