use crate::algebra::abstr::cast::ToPrimitive;
use crate::{algebra::abstr::Real, special::gamma::Gamma};
pub trait Beta {
fn beta(self, y: Self) -> Self;
fn beta_inc(self, a: Self, b: Self) -> Self;
fn beta_inc_reg(self, a: Self, b: Self) -> Self;
}
macro_rules! impl_beta {
($t:ident) => {
impl Beta for $t {
fn beta(self, y: Self) -> Self {
self.gamma() * y.gamma() / (self + y).gamma()
}
fn beta_inc(self, a: Self, b: Self) -> Self {
a.beta(b) * self.beta_inc_reg(a, b)
}
fn beta_inc_reg(self, a: Self, b: Self) -> Self {
let acu: Self = 0.1E-14;
if a <= 0.0 || b <= 0.0 {
panic!();
}
if !(0.0..=1.0).contains(&self) {
panic!();
}
if self == 0.0 || self == 1.0 {
return self;
}
let mut psq: Self = a + b;
let mut cx: Self = 1.0 - self;
let xx: Self;
let pp: Self;
let qq: Self;
let indx: u32;
if a < psq * self {
xx = cx;
cx = self;
pp = b;
qq = a;
indx = 1;
} else {
xx = self;
pp = a;
qq = b;
indx = 0;
}
let mut term: Self = 1.0;
let mut ai: Self = 1.0;
let mut value: Self = 1.0;
let mut ns: i32 = (qq + cx * psq).to_i32();
let mut rx: Self = xx / cx;
let mut temp: Self = qq - ai;
if ns == 0 {
rx = xx;
}
loop {
term = term * temp * rx / (pp + ai);
value += term;
temp = term.abs();
if temp <= acu && temp <= acu * value {
value = value
* (pp * xx.ln() + (qq - 1.0) * cx.ln() - (a.beta(b)).ln()).exp()
/ pp;
if indx != 0 {
value = 1.0 - value;
}
break;
}
ai += 1.0;
ns -= 1;
if 0 <= ns {
temp = qq - ai;
if ns == 0 {
rx = xx;
}
} else {
temp = psq;
psq += 1.0;
}
}
value
}
}
};
}
impl_beta!(f32);
impl_beta!(f64);
pub fn beta<T>(x: T, y: T) -> T
where
T: Real + Beta,
{
x.beta(y)
}
pub fn beta_inc<T>(x: T, a: T, b: T) -> T
where
T: Real + Beta,
{
x.beta_inc(a, b)
}
pub fn beta_inc_reg<T>(x: T, a: T, b: T) -> T
where
T: Real + Beta,
{
x.beta_inc_reg(a, b)
}