use num_traits::Float;
use strafe_type::FloatConstraint;
use crate::distribution::norm::{log_qnorm, qnorm};
fn do_search(
mut y: f64,
z: &mut f64,
p: f64,
dist_max_y: Option<f64>,
incr: f64,
lower_tail: bool,
log: bool,
p_func: &dyn Fn(f64) -> f64,
log_p_func: &dyn Fn(f64) -> f64,
) -> f64 {
{
let left = if lower_tail { *z >= p } else { *z < p };
if left {
loop {
let mut newz = -1.0; if y > 0.0 {
newz = if log {
log_p_func(y - incr)
} else {
p_func(y - incr)
};
} else if y < 0.0 {
y = 0.0;
}
if y == 0.0 || newz.is_nan() || if lower_tail { newz < p } else { newz >= p } {
return y; }
y = (y - incr).max(0.0);
*z = newz;
}
} else {
loop {
y += incr;
if let Some(y_max) = dist_max_y {
if y < y_max {
*z = if log { log_p_func(y) } else { p_func(y) };
} else if y > y_max {
y = y_max
}
} else {
*z = if log { log_p_func(y) } else { p_func(y) };
}
let dist_bool = if let Some(y_max) = dist_max_y {
y == y_max
} else {
false
};
if dist_bool || z.is_nan() || if lower_tail { *z >= p } else { *z < p } {
return y;
}
}
}
}
}
fn discr_check_boundary(y: &mut f64, dist_max_y: Option<f64>) {
if let Some(max_y) = dist_max_y {
if *y > max_y {
*y = max_y;
}
}
if *y < 0.0 {
*y = 0.0;
}
}
pub fn discrete_body(
mu: f64,
sigma: f64,
gamma: f64,
mut p: f64,
dist_max_y: Option<f64>,
lower_tail: bool,
log: bool,
p_func: &dyn Fn(f64) -> f64,
log_p_func: &dyn Fn(f64) -> f64,
) -> f64 {
let mut incr = 0.0;
let mut z = if log {
log_qnorm(p, 0.0, 1.0, lower_tail).unwrap()
} else {
qnorm(p, 0.0, 1.0, lower_tail).unwrap()
};
let mut y = (mu + sigma * (z + gamma * (z * z - 1.0) / 6.0)).round();
discr_check_boundary(&mut y, dist_max_y);
z = if log { log_p_func(y) } else { p_func(y) };
let _pf_n_ = 8;
let _pf_L_ = 2;
let _yLarge_ = 4096;
let _incF_ = 1.0 / 64.0;
let _iShrink_ = 8;
let _relTol_ = 1e-15;
let _xf_ = 4;
if log {
let e = _pf_L_ as f64 * f64::EPSILON;
if lower_tail && p > -f64::max_value() {
p *= 1.0 + e;
} else {
p *= 1.0 - e;
}
} else {
let e = _pf_n_ as f64 * f64::EPSILON;
if lower_tail {
p *= 1.0 - e;
} else if 1.0 - p > _xf_ as f64 * e {
p *= 1.0 + e;
}
}
if y < _yLarge_ as f64 {
do_search(
y, &mut 1.0, p, dist_max_y, incr, lower_tail, log, p_func, log_p_func,
)
}
else {
let mut oldincr;
incr = (y * _incF_).floor();
loop {
oldincr = incr;
y = do_search(
y, &mut z, p, dist_max_y, incr, lower_tail, log, p_func, log_p_func,
);
incr = (incr / _iShrink_ as f64).floor().max(1.0);
if !(oldincr > 1.0 && incr > y * _relTol_) {
break;
}
}
y
}
}