pub fn quantile(sorted: &[f64], p: f64) -> f64 {
let n = sorted.len();
if n == 0 {
return f64::NAN;
}
if n == 1 {
return sorted[0];
}
let h = (n - 1) as f64 * p;
let lo = h.floor() as usize;
let hi = h.ceil() as usize;
sorted[lo] + (h - lo as f64) * (sorted[hi] - sorted[lo])
}
pub const PCG32_MULT: u64 = 6364136223846793005;
pub const PCG32_DEFAULT_STATE: u64 = 0x853c49e6748fea9b;
pub const PCG32_DEFAULT_INC: u64 = 0xda3e39cb94b95bdb;
pub fn lemire_bounded(u32_val: u32, n: usize) -> usize {
((u32_val as u64 * n as u64) >> 32) as usize
}
#[derive(Debug, Clone)]
pub struct Pcg32 {
state: u64,
inc: u64,
}
impl Pcg32 {
pub fn new() -> Self {
Self {
state: PCG32_DEFAULT_STATE,
inc: PCG32_DEFAULT_INC,
}
}
pub fn with_seed(state: u64, inc: u64) -> Self {
Self { state, inc }
}
pub fn next_u32(&mut self) -> u32 {
let old = self.state;
self.state = old.wrapping_mul(PCG32_MULT).wrapping_add(self.inc);
let xorshifted = ((((old >> 18) ^ old) >> 27) & 0xffff_ffff) as u32;
let rot = (old >> 59) as u32; xorshifted.rotate_right(rot)
}
pub fn bounded_index(&mut self, n: usize) -> usize {
lemire_bounded(self.next_u32(), n)
}
pub fn next_f64(&mut self) -> f64 {
self.next_u32() as f64 / 4_294_967_296.0
}
}
impl Default for Pcg32 {
fn default() -> Self {
Self::new()
}
}
#[allow(clippy::excessive_precision)]
pub fn inv_normal(p: f64) -> f64 {
if p <= 0.0 {
return f64::NEG_INFINITY;
}
if p >= 1.0 {
return f64::INFINITY;
}
let a1 = -3.969683028665376e1;
let a2 = 2.209460984245205e2;
let a3 = -2.759285104469687e2;
let a4 = 1.38357751867269e2;
let a5 = -3.066479806614716e1;
let a6 = 2.506628277459239e0;
let b1 = -5.447609879822406e1;
let b2 = 1.615858368580409e2;
let b3 = -1.556989798598866e2;
let b4 = 6.680131188771972e1;
let b5 = -1.328068155288572e1;
let c1 = -7.784894002430293e-3;
let c2 = -3.223964580411365e-1;
let c3 = -2.400758277161838e0;
let c4 = -2.549732539343734e0;
let c5 = 4.374664141464968e0;
let c6 = 2.938163982698783e0;
let d1 = 7.784695709041462e-3;
let d2 = 3.224671290700398e-1;
let d3 = 2.445134137142996e0;
let d4 = 3.754408661907416e0;
let p_low = 0.02425;
let p_high = 1.0 - p_low;
if p < p_low {
let q = (-2.0 * p.ln()).sqrt();
(((((c1 * q + c2) * q + c3) * q + c4) * q + c5) * q + c6)
/ ((((d1 * q + d2) * q + d3) * q + d4) * q + 1.0)
} else if p <= p_high {
let q = p - 0.5;
let r = q * q;
(((((a1 * r + a2) * r + a3) * r + a4) * r + a5) * r + a6) * q
/ (((((b1 * r + b2) * r + b3) * r + b4) * r + b5) * r + 1.0)
} else {
let q = (-2.0 * (1.0 - p).ln()).sqrt();
-(((((c1 * q + c2) * q + c3) * q + c4) * q + c5) * q + c6)
/ ((((d1 * q + d2) * q + d3) * q + d4) * q + 1.0)
}
}
#[allow(clippy::excessive_precision)]
pub fn phi(x: f64) -> f64 {
let xa = x.abs();
if xa > 37.0 {
return if x > 0.0 { 1.0 } else { 0.0 };
}
let e = (-xa * xa / 2.0).exp();
let c: f64;
if xa < 7.07106781186547 {
let mut b = 3.52624965998911e-2 * xa + 0.700383064443688;
b = b * xa + 6.37396220353165;
b = b * xa + 33.912866078383;
b = b * xa + 112.079291497871;
b = b * xa + 221.213596169931;
b = b * xa + 220.206867912376;
let num = e * b;
b = 8.83883476483184e-2 * xa + 1.75566716318264;
b = b * xa + 16.064177579207;
b = b * xa + 86.7807322029461;
b = b * xa + 296.564248779674;
b = b * xa + 637.333633378831;
b = b * xa + 793.826512519948;
b = b * xa + 440.413735824752;
c = num / b;
} else {
let mut b = xa + 0.65;
b = xa + 4.0 / b;
b = xa + 3.0 / b;
b = xa + 2.0 / b;
b = xa + 1.0 / b;
c = e / b / 2.506628274631;
}
if x > 0.0 {
1.0 - c
} else {
c
}
}
pub fn t975(df: usize) -> f64 {
match df {
0 | 1 => 12.706,
2 => 4.303,
3 => 3.182,
4 => 2.776,
5 => 2.571,
6 => 2.447,
_ => 2.365,
}
}
pub fn sum(xs: &[f64]) -> f64 {
let mut s = 0.0;
for &x in xs {
s += x;
}
s
}
pub fn sample_mean(xs: &[f64]) -> f64 {
if xs.is_empty() {
return 0.0;
}
sum(xs) / xs.len() as f64
}
pub fn sample_variance(xs: &[f64]) -> f64 {
let n = xs.len();
if n < 2 {
return 0.0;
}
let m = sample_mean(xs);
let mut s = 0.0;
for &x in xs {
let d = x - m;
s += d * d;
}
s / (n - 1) as f64
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn quantile_hand_computed() {
assert_eq!(quantile(&[1.0, 2.0, 3.0, 4.0], 0.5), 2.5);
assert_eq!(quantile(&[1.0, 2.0, 3.0, 4.0], 0.25), 1.75);
assert_eq!(quantile(&[1.0, 2.0, 3.0, 4.0], 0.0), 1.0);
assert_eq!(quantile(&[1.0, 2.0, 3.0, 4.0], 1.0), 4.0);
assert_eq!(quantile(&[7.0], 0.5), 7.0);
assert!(quantile(&[], 0.5).is_nan());
}
#[test]
fn pcg32_first_outputs() {
let mut r = Pcg32::new();
assert_eq!(
[r.next_u32(), r.next_u32(), r.next_u32(), r.next_u32()],
[355248013, 41705475, 3406281715, 4186697710]
);
}
#[test]
fn lemire_bounds() {
assert_eq!(lemire_bounded(0, 100), 0);
assert_eq!(lemire_bounded(0xffff_ffff, 100), 99);
assert_eq!(lemire_bounded(0x8000_0000, 10), 5);
}
#[test]
fn inv_normal_and_phi_anchors() {
assert_eq!(inv_normal(0.5), 0.0);
assert_eq!(phi(0.0), 0.5);
assert_eq!(inv_normal(0.0), f64::NEG_INFINITY);
assert_eq!(inv_normal(1.0), f64::INFINITY);
assert!((inv_normal(0.975) - 1.959963984540054).abs() < 1e-8);
assert!((phi(1.959963984540054) - 0.975).abs() < 1e-9);
}
#[test]
fn t975_table_and_clamps() {
assert_eq!(t975(1), 12.706);
assert_eq!(t975(0), 12.706);
assert_eq!(t975(2), 4.303);
assert_eq!(t975(7), 2.365);
assert_eq!(t975(99), 2.365);
}
#[test]
fn fixed_order_sums() {
let xs = [2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0];
assert_eq!(sum(&xs), 40.0);
assert_eq!(sample_mean(&xs), 5.0);
assert_eq!(sample_variance(&xs), 32.0 / 7.0);
assert_eq!(sum(&[0.1, 0.1, 0.1]), 0.30000000000000004);
}
}