mod pd;
mod ps;
use thermite::math::policy::Policy;
use thermite::math::{FloatConsts, TranscendentalMathWithPolicy};
use thermite::prelude::*;
use crate::Compensated;
pub trait CompensatedGammaOps: FloatVector + TranscendentalMathWithPolicy {}
impl<T> CompensatedGammaOps for T where T: FloatVector + TranscendentalMathWithPolicy {}
const SHIFT_TARGET: i64 = 30;
#[inline(always)]
fn frac<C: FloatVector, const N: i64, const D: i64>() -> C {
C::splat(const { <C::Element as FloatElement>::ConstRatio::<N, D>::VALUE })
}
#[inline(always)]
fn int_frac<C: FloatVector, const N: i64>() -> C {
C::splat(const { <C::Element as FloatElement>::ConstInt::<N>::VALUE })
}
#[inline(always)]
fn stirling_series<C: FloatVector>(w: C) -> C {
let mut acc = <C as NumericVector>::ZERO;
macro_rules! horner {
($(($n:literal, $d:literal)),* $(,)?) => {
$( acc = acc.mul_add(w, frac::<C, $n, $d>()); )*
};
}
horner!(
(77683, 5796),
(-174611, 125400),
(43867, 244188),
(-3617, 122400),
(1, 156),
(-691, 360360),
(1, 1188),
(-1, 1680),
(1, 1260),
(-1, 360),
(1, 12),
);
acc
}
#[inline(always)]
fn digamma_series<C: FloatVector>(w: C) -> C {
let mut acc = <C as NumericVector>::ZERO;
macro_rules! horner {
($(($n:literal, $d:literal)),* $(,)?) => {
$( acc = acc.mul_add(w, frac::<C, $n, $d>()); )*
};
}
horner!(
(77683, 276),
(-174611, 6600),
(43867, 14364),
(-3617, 8160),
(1, 12),
(-691, 32760),
(1, 132),
(-1, 240),
(1, 252),
(-1, 120),
(1, 12),
);
acc
}
#[inline(always)]
fn trigamma_series<C: FloatVector>(w: C) -> C {
let mut acc = <C as NumericVector>::ZERO;
macro_rules! horner {
($(($n:literal, $d:literal)),* $(,)?) => {
$( acc = acc.mul_add(w, frac::<C, $n, $d>()); )*
};
}
horner!(
(-236364091, 2730),
(854513, 138),
(-174611, 330),
(43867, 798),
(-3617, 510),
(7, 6),
(-691, 2730),
(5, 66),
(-1, 30),
(1, 42),
(-1, 30),
(1, 6),
);
acc
}
pub trait SpecializedCompensatedSpecialMath<E>: Sized {
#[inline(always)]
fn compensated_tgamma<P: Policy>(x: Compensated<Self>) -> Compensated<Self>
where
Compensated<Self>: CompensatedGammaOps,
{
let (lg, sign) = Self::compensated_lgamma_r::<P>(x);
sign * lg.exp_p::<P>()
}
#[inline(always)]
fn compensated_lgamma_r<P: Policy>(x: Compensated<Self>) -> (Compensated<Self>, Compensated<Self>)
where
Compensated<Self>: CompensatedGammaOps,
{
let one = <Compensated<Self> as NumericVector>::ONE;
let half = frac::<Compensated<Self>, 1, 2>();
let reflect = x.cmp_lt(half);
let z0 = reflect.select(one - x, x);
let target = int_frac::<Compensated<Self>, SHIFT_TARGET>();
let mut z = z0;
let mut prod = one;
let mut i = 0;
while i < SHIFT_TARGET {
let shifting = z.cmp_lt(target);
prod = prod.mul_c(shifting, z);
z = z.add_c(shifting, one);
i += 1;
}
let w = one / (z * z);
let poly = stirling_series::<Compensated<Self>>(w);
let half_ln_tau = (<Compensated<Self> as FloatConsts>::LN_2 + <Compensated<Self> as FloatConsts>::LN_PI) * half;
let stirling = (z - half).mul_add(z.ln_p::<P>(), half_ln_tau - z) + poly / z;
let lg = stirling - prod.ln_p::<P>();
let sp = x.sin_pi_p::<P>();
let reflected = (<Compensated<Self> as FloatConsts>::LN_PI - sp.abs().ln_p::<P>()) - lg;
let mut value = reflect.select(reflected, lg);
let sign = one.neg_c(reflect & sp.is_negative());
let zero = <Compensated<Self> as NumericVector>::ZERO;
let is_pole = reflect & x.cmp_le(zero) & x.cmp_eq(x.floor());
value = is_pole.select(<Compensated<Self> as FloatVector>::INFINITY, value);
(value, sign)
}
#[inline(always)]
fn compensated_digamma<P: Policy>(x: Compensated<Self>) -> Compensated<Self>
where
Compensated<Self>: CompensatedGammaOps,
{
let one = <Compensated<Self> as NumericVector>::ONE;
let half = frac::<Compensated<Self>, 1, 2>();
let reflect = x.cmp_lt(half);
let z0 = reflect.select(one - x, x);
let target = int_frac::<Compensated<Self>, SHIFT_TARGET>();
let mut z = z0;
let mut acc = <Compensated<Self> as NumericVector>::ZERO;
let mut i = 0;
while i < SHIFT_TARGET {
let shifting = z.cmp_lt(target);
acc = acc.add_c(shifting, one / z);
z = z.add_c(shifting, one);
i += 1;
}
let w = one / (z * z);
let psi = (z.ln_p::<P>() - half / z) - w * digamma_series::<Compensated<Self>>(w);
let value = psi - acc;
let (sp, cp) = x.sincos_pi_p::<P>();
let reflected = value - <Compensated<Self> as FloatConsts>::PI * (cp / sp);
reflect.select(reflected, value)
}
#[inline(always)]
fn compensated_trigamma<P: Policy>(x: Compensated<Self>) -> Compensated<Self>
where
Compensated<Self>: CompensatedGammaOps,
{
let one = <Compensated<Self> as NumericVector>::ONE;
let half = frac::<Compensated<Self>, 1, 2>();
let reflect = x.cmp_lt(half);
let z0 = reflect.select(one - x, x);
let target = int_frac::<Compensated<Self>, SHIFT_TARGET>();
let mut z = z0;
let mut acc = <Compensated<Self> as NumericVector>::ZERO;
let mut i = 0;
while i < SHIFT_TARGET {
let shifting = z.cmp_lt(target);
acc = acc.add_c(shifting, one / (z * z));
z = z.add_c(shifting, one);
i += 1;
}
let w = one / (z * z);
let psi1 = (one + half / z + w * trigamma_series::<Compensated<Self>>(w)) / z;
let value = psi1 + acc;
let sp = x.sin_pi_p::<P>();
let reflected = <Compensated<Self> as FloatConsts>::PI_SQUARED / (sp * sp) - value;
reflect.select(reflected, value)
}
#[inline(always)]
fn compensated_beta<P: Policy>(a: Compensated<Self>, b: Compensated<Self>) -> Compensated<Self>
where
Compensated<Self>: CompensatedGammaOps,
{
let (la, sa) = Self::compensated_lgamma_r::<P>(a);
let (lb, sb) = Self::compensated_lgamma_r::<P>(b);
let (lab, sab) = Self::compensated_lgamma_r::<P>(a + b);
((la + lb) - lab).exp_p::<P>() * ((sa * sb) / sab)
}
}