use std::hint::spin_loop;
use statrs::distribution::{ContinuousCDF, Normal};
const DEFAULT_THREADS_NUM:usize = 1;
#[allow(dead_code)]
const CPU_2_THREAD_RATIO:usize = 2;
pub fn max_threads() -> usize {
let num_threads:usize = if let Ok(available_cpus) = std::thread::available_parallelism() {
available_cpus.get() * CPU_2_THREAD_RATIO
} else {
DEFAULT_THREADS_NUM
};
num_threads
}
pub struct SpinWait;
#[allow(dead_code)]
impl SpinWait {
pub fn loop_while<F>(predicate:F)
where F:Fn() -> bool {
let mut spins = 0usize;
while predicate() {
Self::spin_backoff(&mut spins);
}
}
pub fn loop_while_mut<F>(mut predicate:F)
where F:FnMut() -> bool {
let mut spins = 0usize;
while predicate() {
Self::spin_backoff(&mut spins);
}
}
fn spin_backoff(spins: &mut usize) {
static MAX_SPINS: usize = 128;
if *spins < MAX_SPINS {
spin_loop(); *spins += 1;
} else {
std::thread::yield_now();
*spins /= 2; }
}
}
fn mean_and_sample_std(data: &[f64]) -> Option<(f64, f64)> {
let n = data.len();
if n == 0 { return None; }
let mean = data.iter().sum::<f64>() / n as f64;
if n == 1 { return Some((mean, 0.0)); }
let var = data.iter().map(|x| {
let d = x - mean;
d * d
}).sum::<f64>() / (n as f64 - 1.0); Some((mean, var.sqrt()))
}
pub fn lower_leg_values_outside_central_interval(data: &[f64], prob: f64) -> Vec<(usize, f64)> {
if data.is_empty() { return Vec::new(); }
let z = normal_curve_z_value(prob);
let (mean, std) = match mean_and_sample_std(data) {
Some(ms) => ms,
None => return Vec::new(),
};
let lower = mean - z * std;
data.iter().cloned().enumerate().filter(|&(_,x)| x < lower).collect()
}
pub fn normal_curve_z_value(prob:f64) -> f64 {
let normal = Normal::new(0.0, 1.0).unwrap();
normal.inverse_cdf((1.0 + prob) / 2.0)
}