#![cfg(feature = "std")]
pub struct Xorshift64 {
state: u64,
}
impl Xorshift64 {
pub fn new(seed: u64) -> Self {
Self {
state: if seed == 0 { 1 } else { seed },
}
}
pub fn next_u64(&mut self) -> u64 {
let mut x = self.state;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
self.state = x;
x
}
pub fn next_f64(&mut self) -> f64 {
let bits = self.next_u64() >> 11;
bits as f64 * (1.0 / (1u64 << 53) as f64)
}
}
pub struct GaussianNoise {
rng: Xorshift64,
spare: Option<f64>,
}
impl GaussianNoise {
pub fn new(seed: u64) -> Self {
Self {
rng: Xorshift64::new(seed),
spare: None,
}
}
pub fn sample(&mut self, sigma: f64) -> f64 {
if let Some(spare) = self.spare.take() {
return spare * sigma;
}
let u1 = loop {
let u = self.rng.next_f64();
if u > 1e-15 {
break u;
}
};
let u2 = self.rng.next_f64();
let mag = (-2.0 * u1.ln()).sqrt();
let theta = core::f64::consts::TAU * u2;
let z0 = mag * theta.cos();
let z1 = mag * theta.sin();
self.spare = Some(z1);
z0 * sigma
}
pub fn add_noise(&mut self, signal: f64, amplitude: f64) -> f64 {
signal + self.sample(amplitude)
}
}
pub struct UniformNoise {
rng: Xorshift64,
}
impl UniformNoise {
pub fn new(seed: u64) -> Self {
Self {
rng: Xorshift64::new(seed),
}
}
pub fn sample(&mut self, amplitude: f64) -> f64 {
let u = self.rng.next_f64() * 2.0 - 1.0;
u * amplitude
}
pub fn add_noise(&mut self, signal: f64, amplitude: f64) -> f64 {
signal + self.sample(amplitude)
}
}
pub struct OutlierNoise {
rng: Xorshift64,
pub rate: f64,
pub amplitude: f64,
}
impl OutlierNoise {
pub fn new(seed: u64, rate: f64, amplitude: f64) -> Self {
Self {
rng: Xorshift64::new(seed),
rate,
amplitude,
}
}
pub fn add_noise(&mut self, signal: f64) -> f64 {
let u = self.rng.next_f64();
if u < self.rate {
let sign = if self.rng.next_f64() < 0.5 { 1.0 } else { -1.0 };
sign * self.amplitude
} else {
signal
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_xorshift_deterministic() {
let mut rng1 = Xorshift64::new(42);
let mut rng2 = Xorshift64::new(42);
for _ in 0..100 {
assert_eq!(rng1.next_u64(), rng2.next_u64());
}
}
#[test]
fn test_gaussian_mean_and_variance() {
let mut noise = GaussianNoise::new(12345);
let sigma = 2.0_f64;
let n = 50_000;
let samples: Vec<f64> = (0..n).map(|_| noise.sample(sigma)).collect();
let mean = samples.iter().sum::<f64>() / n as f64;
let var = samples.iter().map(|&x| (x - mean).powi(2)).sum::<f64>() / n as f64;
assert!(mean.abs() < 0.05 * sigma, "mean={mean}");
assert!(
(var - sigma * sigma).abs() < 0.15 * sigma * sigma,
"var={var}"
);
}
#[test]
fn test_uniform_bounds() {
let mut noise = UniformNoise::new(99);
let amp = 3.0_f64;
for _ in 0..10_000 {
let s = noise.sample(amp);
assert!(s >= -amp && s < amp, "out of bounds: {s}");
}
}
#[test]
fn test_outlier_rate() {
let rate = 0.05_f64;
let mut noise = OutlierNoise::new(7, rate, 100.0);
let signal = 1.0_f64;
let n = 50_000;
let outliers = (0..n)
.filter(|_| {
let v = noise.add_noise(signal);
(v - signal).abs() > 50.0
})
.count();
let measured_rate = outliers as f64 / n as f64;
assert!(
(measured_rate - rate).abs() < 0.01,
"measured_rate={measured_rate}"
);
}
#[test]
fn test_uniform_noise_add() {
let mut noise = UniformNoise::new(1);
let signal = 5.0_f64;
let amp = 0.1_f64;
let result = noise.add_noise(signal, amp);
assert!((result - signal).abs() <= amp + 1e-12);
}
}