use rand::distr::Uniform;
use rand::prelude::*;
use rand_distr::Normal;
pub trait Sampling<R: rand::Rng = ThreadRng> {
fn sample(&self, rng: &mut R, n: usize) -> Vec<f64>;
}
pub trait PDF {
fn fit(&mut self, data: &[f64]);
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct UniformDistribution {
pub lower: f64,
pub upper: f64,
}
impl<R: Rng> Sampling<R> for UniformDistribution {
fn sample(&self, rng: &mut R, n: usize) -> Vec<f64> {
let uni = Uniform::new(self.lower, self.upper).unwrap();
(0..n).map(|_| rng.sample(uni)).collect()
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct NormalDistribution {
pub mu: f64,
pub sigma: f64,
}
impl<R: Rng> Sampling<R> for NormalDistribution {
fn sample(&self, rng: &mut R, n: usize) -> Vec<f64> {
let normal = Normal::new(self.mu, self.sigma).unwrap();
normal.sample_iter(rng).take(n).collect()
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Poisson {
pub lambda: f64,
}
impl Sampling for Poisson {
fn sample(&self, rng: &mut ThreadRng, n: usize) -> Vec<f64> {
let mut samples = Vec::with_capacity(n);
let exp_neg_lambda = (-self.lambda).exp();
for _ in 0..n {
let mut x = 0;
let mut p = 1.0;
loop {
let u = rng.random::<f64>();
p *= u;
if p <= exp_neg_lambda {
break;
}
x += 1;
}
samples.push(x as f64);
}
samples
}
}
impl PDF for Poisson {
fn fit(&mut self, data: &[f64]) {
let sum: f64 = data.iter().sum();
let count = data.len() as f64;
self.lambda = if count > 0.0 { sum / count } else { 0.0 };
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Exponential {
pub lambda: f64,
}
impl Sampling for Exponential {
fn sample(&self, rng: &mut ThreadRng, n: usize) -> Vec<f64> {
let mut samples = Vec::with_capacity(n);
let inv_lambda = -1.0 / self.lambda;
for _ in 0..n {
samples.push(inv_lambda * rng.random::<f64>().ln());
}
samples
}
}