1use arrayref::array_mut_ref;
24use rand::Rng;
25use rand_chacha::{rand_core::SeedableRng, ChaChaRng};
26use std::{
27 cmp::Ordering,
28 ops::{Add, AddAssign},
29 time::Duration,
30};
31
32pub const DELAY_SEED_SIZE: usize = 16;
33pub type DelaySeed = [u8; DELAY_SEED_SIZE];
34
35#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
37pub struct Delay(f64);
38
39impl Delay {
40 pub fn zero() -> Self {
42 Self(0.0)
43 }
44
45 pub fn exp(seed: &DelaySeed) -> Self {
48 let mut double_seed = [0; 32];
52 *array_mut_ref![double_seed, 0, 16] = *seed;
53 *array_mut_ref![double_seed, 16, 16] = *seed;
54 let mut rng = ChaChaRng::from_seed(double_seed);
55 let delay: f64 = rng.sample(rand_distr::Exp1);
56 Self(delay.min(10.0))
59 }
60
61 pub fn to_duration(self, unit: Duration) -> Duration {
64 unit.mul_f64(self.0)
65 }
66}
67
68impl Eq for Delay {}
70
71#[allow(clippy::derive_ord_xor_partial_ord)]
72impl Ord for Delay {
73 fn cmp(&self, other: &Self) -> Ordering {
74 self.partial_cmp(other).expect("Delays are never NaN")
75 }
76}
77
78impl Add for Delay {
79 type Output = Self;
80
81 fn add(self, other: Self) -> Self {
82 Self(self.0 + other.0)
83 }
84}
85
86impl AddAssign for Delay {
87 fn add_assign(&mut self, other: Self) {
88 self.0 += other.0;
89 }
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95
96 #[test]
97 fn portable_deterministic_exp() {
98 assert_eq!(
99 Delay::exp(&[
100 0xdc, 0x18, 0x0e, 0xe6, 0x71, 0x1e, 0xcf, 0x2d, 0xad, 0x0c, 0xde, 0xd1, 0xd4, 0x94,
101 0xbd, 0x3b
102 ]),
103 Delay(2.953842296445717)
104 );
105 assert_eq!(
106 Delay::exp(&[
107 0x0a, 0xcc, 0x48, 0xbd, 0xa2, 0x30, 0x9a, 0x48, 0xc8, 0x78, 0x61, 0x0d, 0xf8, 0xc2,
108 0x8d, 0x99
109 ]),
110 Delay(1.278588765412407)
111 );
112 assert_eq!(
113 Delay::exp(&[
114 0x17, 0x4c, 0x40, 0x2f, 0x8f, 0xda, 0xa6, 0x46, 0x45, 0xe7, 0x1c, 0xb0, 0x1e, 0xff,
115 0xf8, 0xfc
116 ]),
117 Delay(0.7747915675800142)
118 );
119 assert_eq!(
120 Delay::exp(&[
121 0xca, 0xe8, 0x07, 0x72, 0x17, 0x28, 0xf7, 0x09, 0xd8, 0x7d, 0x3e, 0xa2, 0x03, 0x7d,
122 0x4f, 0x03
123 ]),
124 Delay(0.8799379598933348)
125 );
126 assert_eq!(
127 Delay::exp(&[
128 0x61, 0x56, 0x54, 0x41, 0xd0, 0x25, 0xdf, 0xe7, 0xb9, 0xc8, 0x6a, 0x56, 0xdd, 0x27,
129 0x09, 0xa6
130 ]),
131 Delay(10.0)
132 );
133 }
134}