rta_for_fps_lib/
window.rs

1//! Module defining the Window and its operations
2
3use core::fmt::Debug;
4use core::marker::PhantomData;
5
6use crate::time::{TimeUnit, UnitNumber};
7use crate::window::window_types::WindowType;
8
9pub mod window_types {
10    //!  Module for the `WindowType` trait
11
12    use core::fmt::Debug;
13
14    use crate::seal::Seal;
15    use crate::window::{Demand, Overlap, Supply};
16
17    /// Marker Trait for Window Types
18    pub trait WindowType: Seal + Debug {}
19
20    impl WindowType for Supply {}
21
22    impl WindowType for Demand {}
23
24    impl<P: WindowType, Q: WindowType> WindowType for Overlap<P, Q> {}
25}
26
27mod window_end;
28
29pub use window_end::WindowEnd;
30
31/// Type representing a Window based on the papers Definition 1.
32///
33/// With an extra Type Parameter to indicate the Window type
34// Not Copy to prevent accidental errors due to implicit copy
35#[derive(Debug, Hash, Eq)]
36pub struct Window<T> {
37    /// The Start point of the Window
38    pub start: TimeUnit,
39    /// The End Point of the Window
40    pub end: WindowEnd,
41    /// The Kind of the Window
42    window_type: PhantomData<T>,
43}
44
45impl<W> PartialEq for Window<W> {
46    fn eq(&self, other: &Self) -> bool {
47        self.start == other.start && self.end == other.end
48    }
49}
50
51impl<T> Clone for Window<T> {
52    fn clone(&self) -> Self {
53        Window {
54            start: self.start,
55            end: self.end,
56            window_type: PhantomData,
57        }
58    }
59}
60
61impl<T> Window<T> {
62    /// Create a new Window
63    #[must_use]
64    pub fn new<I: Into<TimeUnit>, E: Into<WindowEnd>>(start: I, end: E) -> Self {
65        Window {
66            start: start.into(),
67            end: end.into(),
68            window_type: PhantomData,
69        }
70    }
71
72    /// Create a new empty Window
73    #[must_use]
74    pub const fn empty() -> Self {
75        Window {
76            start: TimeUnit::ZERO,
77            end: WindowEnd::Finite(TimeUnit::ZERO),
78            window_type: PhantomData,
79        }
80    }
81
82    /// Calculate the window length as defined in Definition 1. of the paper
83    #[must_use]
84    pub fn length(&self) -> WindowEnd {
85        match self.end {
86            WindowEnd::Finite(end) => {
87                let end = if self.start < end {
88                    end - self.start
89                } else {
90                    TimeUnit::ZERO
91                };
92                WindowEnd::Finite(end)
93            }
94            WindowEnd::Infinite => WindowEnd::Infinite,
95        }
96    }
97
98    /// Calculate the overlap (Ω) of two windows as defined in Definition 2. of the paper
99    #[must_use]
100    pub fn overlaps(&self, other: &Self) -> bool {
101        !(self.end < other.start || other.end < self.start)
102    }
103
104    /// Determine if two windows are adjacent, a special case of overlapping
105    ///
106    /// Used by `AggregationIterator` to take advantage of the relaxed invariant of `CurveIterator` as opposed to `Curve`
107    #[must_use]
108    pub fn adjacent(&self, other: &Self) -> bool {
109        self.end == other.start || self.start == other.end
110    }
111
112    /// Calculate the Window delta as defined in Definition 6. of the paper
113    #[must_use]
114    pub fn delta<Q: WindowType>(supply: &Self, demand: &Window<Q>) -> WindowDeltaResult<T, Q>
115    where
116        T: WindowType,
117    {
118        if supply.end < demand.start {
119            WindowDeltaResult {
120                remaining_supply_head: supply.clone(),
121                remaining_supply_tail: Window::empty(),
122                overlap: Window::empty(),
123                remaining_demand: demand.clone(),
124            }
125        } else {
126            let overlap_start = TimeUnit::max(supply.start, demand.start);
127            let overlap_end: WindowEnd =
128                overlap_start + WindowEnd::min(demand.length(), supply.end - overlap_start);
129            let overlap = Window::new(overlap_start, overlap_end);
130
131            let remaining_demand = match overlap.length() {
132                WindowEnd::Finite(length) => Window::new(demand.start + length, demand.end),
133                WindowEnd::Infinite => {
134                    // Infinite supply satisfies infinite demand, no demand left
135
136                    Window::empty()
137                }
138            };
139
140            let remaining_supply_head = Window::new(supply.start, overlap.start);
141            let remaining_supply_tail = match overlap.end {
142                WindowEnd::Finite(overlap_end) => Window::new(overlap_end, supply.end),
143                WindowEnd::Infinite => {
144                    // Infinite supply satisfies infinite demand, no tail supply left
145                    Window::empty()
146                }
147            };
148
149            WindowDeltaResult {
150                remaining_supply_head,
151                remaining_supply_tail,
152                overlap,
153                remaining_demand,
154            }
155        }
156    }
157
158    /// Whether the window is empty/has a length of 0
159    #[must_use]
160    pub fn is_empty(&self) -> bool {
161        self.length() == TimeUnit::ZERO
162    }
163
164    /// Calculate the Budget Group that the window falls into
165    /// given a splitting interval
166    ///
167    /// See Section 6.2 §3
168    #[must_use]
169    pub fn budget_group(&self, interval: TimeUnit) -> UnitNumber {
170        self.start / interval
171    }
172
173    /// Calculate the aggregation (⊕) of two windows as defined in Definition 4. of the paper
174    #[must_use]
175    pub fn aggregate(&self, other: &Self) -> Option<Self> {
176        // only defined for overlapping windows, return None when not overlapping
177        self.overlaps(other).then(|| {
178            let start = TimeUnit::min(self.start, other.start);
179            let end = start + self.length() + other.length();
180            Window::new(start, end)
181        })
182    }
183
184    pub fn reclassify<R>(self) -> Window<R> {
185        Window {
186            start: self.start,
187            end: self.end,
188            window_type: PhantomData,
189        }
190    }
191}
192
193/// The Return Type for the [`Window::delta`] calculation
194#[derive(Debug, Eq, PartialEq)] // Eq for tests
195pub struct WindowDeltaResult<P: WindowType, Q: WindowType> {
196    /// The unused supply at the start of the original supply window
197    pub remaining_supply_head: Window<P>,
198    /// The unused supply at the end of the original supply window
199    pub remaining_supply_tail: Window<P>,
200    /// The Windows Overlap
201    pub overlap: Window<Overlap<P, Q>>,
202    /// The unfulfilled "demand"
203    pub remaining_demand: Window<Q>,
204}
205
206/// Marker Type for Window, indicating a Supply Window
207#[derive(Clone, Debug, Eq, PartialEq)]
208pub struct Supply;
209
210/// Marker Type for Window, indicating Demand
211#[derive(Clone, Debug, Eq, PartialEq)]
212pub struct Demand;
213
214/// Marker Type for Window,indicating an Overlap between Supply and Demand
215#[derive(Clone, Debug, Eq, PartialEq)]
216pub struct Overlap<P, Q>(PhantomData<(P, Q)>);