use arrayref::array_mut_ref;
use rand::Rng;
use rand_chacha::{rand_core::SeedableRng, ChaChaRng};
use std::{
cmp::Ordering,
ops::{Add, AddAssign},
time::Duration,
};
pub const DELAY_SEED_SIZE: usize = 16;
pub type DelaySeed = [u8; DELAY_SEED_SIZE];
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
pub struct Delay(f64);
impl Delay {
pub fn zero() -> Self {
Self(0.0)
}
pub fn exp(seed: &DelaySeed) -> Self {
let mut double_seed = [0; 32];
*array_mut_ref![double_seed, 0, 16] = *seed;
*array_mut_ref![double_seed, 16, 16] = *seed;
let mut rng = ChaChaRng::from_seed(double_seed);
let delay: f64 = rng.sample(rand_distr::Exp1);
Self(delay.min(10.0))
}
pub fn to_duration(self, unit: Duration) -> Duration {
unit.mul_f64(self.0)
}
}
impl Eq for Delay {}
#[allow(clippy::derive_ord_xor_partial_ord)]
impl Ord for Delay {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).expect("Delays are never NaN")
}
}
impl Add for Delay {
type Output = Self;
fn add(self, other: Self) -> Self {
Self(self.0 + other.0)
}
}
impl AddAssign for Delay {
fn add_assign(&mut self, other: Self) {
self.0 += other.0;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn portable_deterministic_exp() {
assert_eq!(
Delay::exp(&[
0xdc, 0x18, 0x0e, 0xe6, 0x71, 0x1e, 0xcf, 0x2d, 0xad, 0x0c, 0xde, 0xd1, 0xd4, 0x94,
0xbd, 0x3b
]),
Delay(2.953842296445717)
);
assert_eq!(
Delay::exp(&[
0x0a, 0xcc, 0x48, 0xbd, 0xa2, 0x30, 0x9a, 0x48, 0xc8, 0x78, 0x61, 0x0d, 0xf8, 0xc2,
0x8d, 0x99
]),
Delay(1.278588765412407)
);
assert_eq!(
Delay::exp(&[
0x17, 0x4c, 0x40, 0x2f, 0x8f, 0xda, 0xa6, 0x46, 0x45, 0xe7, 0x1c, 0xb0, 0x1e, 0xff,
0xf8, 0xfc
]),
Delay(0.7747915675800142)
);
assert_eq!(
Delay::exp(&[
0xca, 0xe8, 0x07, 0x72, 0x17, 0x28, 0xf7, 0x09, 0xd8, 0x7d, 0x3e, 0xa2, 0x03, 0x7d,
0x4f, 0x03
]),
Delay(0.8799379598933348)
);
assert_eq!(
Delay::exp(&[
0x61, 0x56, 0x54, 0x41, 0xd0, 0x25, 0xdf, 0xe7, 0xb9, 0xc8, 0x6a, 0x56, 0xdd, 0x27,
0x09, 0xa6
]),
Delay(10.0)
);
}
}