use thermite::math::policy::Policy;
use thermite_special::specialized::{SpecializedRealSpecialMath, SpecializedSpecialMath};
use thermite_special::{RealSpecialMathWithPolicy, SpecialMathWithPolicy};
use thermite::prelude::*;
use crate::Dual;
use crate::math::DualMathVector;
pub trait DualSpecialVector: DualMathVector + SpecialMathWithPolicy + RealSpecialMathWithPolicy {}
impl<V> DualSpecialVector for V where V: DualMathVector + SpecialMathWithPolicy + RealSpecialMathWithPolicy {}
impl<V, E, const N: usize> SpecializedSpecialMath<Dual<E, N>> for Dual<V, N>
where
V: DualSpecialVector + FloatVector<Element = E> + SpecializedSpecialMath<E>,
{
type ExpIntDetails = Self;
#[inline(always)]
fn erf<P: Policy>(self) -> Self {
let v = self.re.erf_p::<P>();
let factor = V::FRAC_2_SQRT_PI * (self.re * self.re).neg().exp_p::<P>();
self.chain(v, factor)
}
#[inline(always)]
fn expint<P: Policy, const M: usize>(self) -> Self {
let (v, prev) = <V as SpecializedSpecialMath<E>>::expint_primal::<P, M>(self.re);
self.chain(v, -prev)
}
#[inline(always)]
fn lambert_w<P: Policy>(self) -> (Self, Self) {
let (w0, wm1) = self.re.lambert_w_p::<P>();
let f0 = w0 / self.re.mul_adde(w0, self.re);
let fm1 = wm1 / self.re.mul_adde(wm1, self.re);
(self.chain(w0, f0), self.chain(wm1, fm1))
}
#[inline(always)]
fn tgamma<P: Policy>(self) -> Self {
let v = self.re.tgamma_p::<P>();
self.chain(v, v * self.re.digamma_p::<P>())
}
#[inline(always)]
fn lgamma<P: Policy>(self) -> Self {
let v = self.re.lgamma_p::<P>();
self.chain(v, self.re.digamma_p::<P>())
}
#[inline(always)]
fn digamma<P: Policy>(self) -> Self {
let v = self.re.digamma_p::<P>();
self.chain(v, SpecializedSpecialMath::trigamma::<P>(self.re))
}
#[inline(always)]
fn beta<P: Policy>(a: Self, b: Self) -> Self {
let v = a.re.beta_p::<P>(b.re);
let psi_ab = (a.re + b.re).digamma_p::<P>();
let fa = v * (a.re.digamma_p::<P>() - psi_ab);
let fb = v * (b.re.digamma_p::<P>() - psi_ab);
let mut dual = a.dual;
let mut i = 0;
while i < N {
dual[i] = fa.mul_adde(a.dual[i], fb * b.dual[i]);
i += 1;
}
Dual { re: v, dual }
}
#[inline(always)]
fn trigamma<P: Policy>(self) -> Self {
todo!("Dual trigamma requires the tetragamma function psi_2; see polygamma")
}
}
impl<V, E, const N: usize> SpecializedRealSpecialMath<Dual<E, N>> for Dual<V, N>
where
V: DualSpecialVector + FloatVector<Element = E> + SpecializedSpecialMath<E>,
{
#[inline(always)]
fn erfinv<P: Policy>(self) -> Self {
let v = self.re.erfinv_p::<P>();
let factor = V::FRAC_SQRT_PI_2 * (v * v).exp_p::<P>();
self.chain(v, factor)
}
#[inline(always)]
fn probit<P: Policy>(self) -> Self {
let v = self.re.probit_p::<P>();
let factor = V::SQRT_TAU * (v * v * V::HALF).exp_p::<P>();
self.chain(v, factor)
}
#[inline(always)]
fn lgamma_r<P: Policy>(self) -> (Self, Self) {
let (v, sign) = self.re.lgamma_r_p::<P>();
(self.chain(v, self.re.digamma_p::<P>()), Self::constant(sign))
}
}
impl<V, E: 'static, const N: usize> thermite_special::specialized::ExpIntDetails<Dual<E, N>, Dual<V, N>>
for Dual<V, N>
where
Dual<V, N>: thermite::vector::FloatVector<Element = Dual<E, N>>,
{
}