use num_traits::Float;
use strafe_type::{FloatConstraint, LogProbability64, Positive64, Probability64, Real64};
use crate::traits::DPQ;
pub fn pnorm<R1: Into<Real64>, R2: Into<Real64>, P: Into<Positive64>>(
x: R1,
mu: R2,
sigma: P,
lower_tail: bool,
) -> Probability64 {
pnorm_inner(x, mu, sigma, lower_tail, false).into()
}
pub fn log_pnorm<R1: Into<Real64>, R2: Into<Real64>, P: Into<Positive64>>(
x: R1,
mu: R2,
sigma: P,
lower_tail: bool,
) -> LogProbability64 {
pnorm_inner(x, mu, sigma, lower_tail, true).into()
}
fn pnorm_inner<R1: Into<Real64>, R2: Into<Real64>, P: Into<Positive64>>(
x: R1,
mu: R2,
sigma: P,
lower_tail: bool,
log: bool,
) -> f64 {
let mut x = x.into().unwrap();
let mu = mu.into().unwrap();
let sigma = sigma.into().unwrap();
let mut p = 0.0;
let mut cp = 0.0;
if !x.is_finite() && mu == x {
return f64::nan();
}
if sigma <= 0.0 {
if sigma < 0.0 {
return f64::nan();
}
return if x < mu {
f64::dt_0(lower_tail, log)
} else {
f64::dt_1(lower_tail, log)
};
}
p = (x - mu) / sigma;
if !p.is_finite() {
return if x < mu {
f64::dt_0(lower_tail, log)
} else {
f64::dt_1(lower_tail, log)
};
}
x = p;
pnorm_both(x, &mut p, &mut cp, if lower_tail { 0 } else { 1 }, log);
if lower_tail {
p
} else {
cp
}
}
pub fn pnorm_both(x: f64, cum: &mut f64, ccum: &mut f64, i_tail: i32, log: bool) {
static a: [f64; 5] = [
2.2352520354606839287,
161.02823106855587881,
1067.6894854603709582,
18154.981253343561249,
0.065682337918207449113,
];
static b: [f64; 4] = [
47.20258190468824187,
976.09855173777669322,
10260.932208618978205,
45507.789335026729956,
];
static c: [f64; 9] = [
0.39894151208813466764,
8.8831497943883759412,
93.506656132177855979,
597.27027639480026226,
2494.5375852903726711,
6848.1904505362823326,
11602.651437647350124,
9842.7148383839780218,
1.0765576773720192317e-8,
];
static d: [f64; 8] = [
22.266688044328115691,
235.38790178262499861,
1519.377599407554805,
6485.558298266760755,
18615.571640885098091,
34900.952721145977266,
38912.003286093271411,
19685.429676859990727,
];
static p: [f64; 6] = [
0.21589853405795699,
0.1274011611602473639,
0.022235277870649807,
0.001421619193227893466,
2.9112874951168792e-5,
0.02307344176494017303,
];
static q: [f64; 5] = [
1.28426009614491121,
0.468238212480865118,
0.0659881378689285515,
0.00378239633202758244,
7.29751555083966205e-5,
];
let mut xden = 0.0;
let mut xnum = 0.0;
let mut temp = 0.0;
let mut del = 0.0;
let mut eps = 0.0;
let mut xsq = 0.0;
let mut y = 0.0;
let mut i = 0;
let mut lower = 0;
let mut upper = 0;
if x.is_nan() {
*ccum = x;
*cum = *ccum;
return;
}
eps = 2.2204460492503131e-16 * 0.5;
lower = !i_tail;
upper = i_tail;
let d_2 = |x: f64| x.ldexp(-1.0);
let mut do_del = |X: f64, xsq: &mut f64, cum: &mut f64, ccum: &mut f64, temp: f64| {
*xsq = X.ldexp(4.0).trunc().ldexp(-4.0);
del = (X - *xsq) * (X + *xsq);
if log {
*cum = (-*xsq * d_2(*xsq)) - d_2(del) + temp.ln();
if (lower != 0 && x > 0.0) || (upper != 0 && x <= 0.0) {
*ccum = (-(-*xsq * d_2(*xsq)).exp() * (-d_2(del)).exp() * temp).ln_1p();
}
} else {
*cum = (-*xsq * d_2(*xsq)).exp() * (-d_2(del)).exp() * temp;
*ccum = 1.0 - *cum;
}
};
let swap_tail = |cum: &mut f64, ccum: &mut f64, temp: &mut f64| {
if x > 0.0 {
*temp = *cum;
if lower != 0 {
*cum = *ccum
}
*ccum = *temp
}
};
y = x.abs();
if y <= 0.67448975 {
if y > eps {
xsq = x * x;
xnum = a[4] * xsq;
xden = xsq;
i = 0;
while i < 3 {
xnum = (xnum + a[i]) * xsq;
xden = (xden + b[i]) * xsq;
i += 1
}
} else {
xden = 0.0;
xnum = xden
}
temp = x * (xnum + a[3]) / (xden + b[3]);
if lower != 0 {
*cum = 0.5 + temp
}
if upper != 0 {
*ccum = 0.5 - temp
}
if log {
if lower != 0 {
*cum = (*cum).ln()
}
if upper != 0 {
*ccum = (*ccum).ln()
}
}
} else if y <= strafe_consts::SQRT_32 {
xnum = c[8] * y;
xden = y;
i = 0;
while i < 7 {
xnum = (xnum + c[i]) * y;
xden = (xden + d[i]) * y;
i += 1
}
temp = (xnum + c[7]) / (xden + d[7]);
do_del(y, &mut xsq, cum, ccum, temp);
swap_tail(cum, ccum, &mut temp);
} else if log && y < 1e170
|| lower != 0 && -37.5193 < x && x < 8.2924
|| upper != 0 && -8.2924 < x && x < 37.5193
{
xsq = 1.0 / (x * x);
xnum = p[5] * xsq;
xden = xsq;
i = 0;
while i < 4 {
xnum = (xnum + p[i]) * xsq;
xden = (xden + q[i]) * xsq;
i += 1
}
temp = xsq * (xnum + p[4]) / (xden + q[4]);
temp = (strafe_consts::_1DSQRT_2TPI - temp) / y;
do_del(x, &mut xsq, cum, ccum, temp);
swap_tail(cum, ccum, &mut temp);
} else if x > 0.0 {
*cum = f64::d_1(log);
*ccum = f64::d_0(log)
} else {
*cum = f64::d_0(log);
*ccum = f64::d_1(log)
};
}