Skip to main content

des_sim/modeling/sampler/combinator/
delay.rs

1//! The `delay` module provides the `DelaySampler`, a combinator that introduces
2//! a positive delay to the output of a base `DurationSampler`.
3//!
4//! This sampler adds a value from a separate delay sampler to the base sample.
5//! Negative delays are treated as zero, ensuring that the resulting duration
6//! is never reduced by the delay component.
7
8use crate::modeling::sampler::{DurationSampler, PendingDuration};
9use crate::primitive::time::SimTime;
10use rand::Rng;
11
12/// A sampler that delays the output of a base sampler by adding the value from a delay sampler.
13///
14/// If the delay sampler returns a negative value, it is treated as zero, meaning the delay
15/// is not applied. If you need to support negative values (e.g., to accelerate the schedule),
16/// use [JitterSampler](crate::modeling::sampler::JitterSampler) instead.
17pub struct DelaySampler<S>
18where
19    S: DurationSampler,
20{
21    sampler: S,
22    delay_sampler: Box<dyn DurationSampler>,
23}
24
25impl<S> DurationSampler for DelaySampler<S>
26where
27    S: DurationSampler,
28{
29    fn sample(&mut self, rng: &mut dyn Rng, current_tick: SimTime) -> PendingDuration {
30        let sampled = self.sampler.sample(rng, current_tick);
31        let delay = self.delay_sampler.sample(rng, current_tick);
32
33        // If the delay is negative, it is interpreted as no delay (max(0.0)).
34        PendingDuration::new(sampled.raw_value() + delay.raw_value().max(0.0))
35    }
36}
37
38impl<S> DelaySampler<S>
39where
40    S: DurationSampler,
41{
42    /// Creates a new `DelaySampler`.
43    pub fn new(sampler: S, delay_sampler: Box<dyn DurationSampler>) -> Self {
44        DelaySampler {
45            sampler,
46            delay_sampler,
47        }
48    }
49}
50
51#[cfg(test)]
52mod tests {
53    use super::*;
54    use crate::modeling::sampler::CombinatorExt;
55    use crate::modeling::sampler::instance::ConstantSampler;
56    use rand::SeedableRng;
57    use rand::rngs::SmallRng;
58
59    #[test]
60    fn test_delay_sampler_positive_delay() {
61        let mut rng = SmallRng::seed_from_u64(2);
62        let base_sampler = ConstantSampler::new(10.0);
63        let delay_sampler = ConstantSampler::new(5.0);
64        let mut sampler = DelaySampler::new(base_sampler, delay_sampler.boxed());
65
66        let sample = sampler.sample(&mut rng, SimTime::from_ticks(0));
67        assert_eq!(sample.raw_value(), 15.0); // 10.0 + 5.0
68    }
69
70    #[test]
71    fn test_delay_sampler_zero_delay() {
72        let mut rng = SmallRng::seed_from_u64(2);
73        let base_sampler = ConstantSampler::new(10.0);
74        let delay_sampler = ConstantSampler::new(0.0);
75        let mut sampler = DelaySampler::new(base_sampler, delay_sampler.boxed());
76
77        let sample = sampler.sample(&mut rng, SimTime::from_ticks(0));
78        assert_eq!(sample.raw_value(), 10.0); // 10.0 + 0.0
79    }
80
81    #[test]
82    fn test_delay_sampler_negative_delay() {
83        let mut rng = SmallRng::seed_from_u64(2);
84        let base_sampler = ConstantSampler::new(10.0);
85        let delay_sampler = ConstantSampler::new(-5.0);
86        let mut sampler = DelaySampler::new(base_sampler, delay_sampler.boxed());
87
88        let sample = sampler.sample(&mut rng, SimTime::from_ticks(0));
89        assert_eq!(sample.raw_value(), 10.0); // 10.0 + max(-5.0, 0.0) = 10.0
90    }
91}