willow_data_model/groupings/
area_of_interest.rs

1use core::cmp::Ordering;
2use core::fmt::Debug;
3use core::hash::Hash;
4
5#[cfg(feature = "dev")]
6use arbitrary::Arbitrary;
7
8use order_theory::{
9    GreatestElement, LowerSemilattice, PredecessorExceptForLeast, SuccessorExceptForGreatest,
10};
11
12use crate::prelude::*;
13
14pub use core::num::NonZeroU64;
15
16/// An [`AreaOfInterest`](https://willowprotocol.org/specs/grouping-entries/#Area) is an [`Area`] together with a maximum number of entries it may include, and a maximum sum of total [payload_lengths](https://willowprotocol.org/specs/data-model/index.html#entry_payload_length) for the included entries.
17///
18/// A [`max_count`](https://willowprotocol.org/specs/grouping-entries/#aoi_count) of [`u64::MAX`] indicates that there is no limit on the number of entries. A [`max_size`](https://willowprotocol.org/specs/grouping-entries/#aoi_size) of [`u64::MAX`] indicates that there is no limit on the total [payload_lengths](https://willowprotocol.org/specs/data-model/index.html#entry_payload_length) of the entries.
19///
20/// ```
21/// use willow_data_model::prelude::*;
22///
23/// let aoi = AreaOfInterest::new(
24///     Area::<2, 2, 2, u8>::new(None, Path::new(), TimeRange::new_closed(0.into(), 17.into())),
25///     NonZeroU64::new(5).unwrap(),
26///     400,
27/// );
28///
29/// assert_eq!(aoi.area(), &Area::<2, 2, 2, u8>::new(None, Path::new(), TimeRange::new_closed(0.into(), 17.into())));
30/// assert_eq!(aoi.max_count(), NonZeroU64::new(5).unwrap());
31/// assert_eq!(aoi.max_size(), 400);
32/// ```
33///
34/// [Specification](https://willowprotocol.org/specs/grouping-entries/#aois)
35#[derive(Clone, Debug, PartialEq, Eq, Hash)]
36#[cfg_attr(feature = "dev", derive(Arbitrary))]
37pub struct AreaOfInterest<const MCL: usize, const MCC: usize, const MPL: usize, S> {
38    area: Area<MCL, MCC, MPL, S>,
39    max_count: NonZeroU64,
40    max_size: u64,
41}
42
43impl<const MCL: usize, const MCC: usize, const MPL: usize, S> AreaOfInterest<MCL, MCC, MPL, S> {
44    /// Creates a new [`AreaOfInterest`] from its constituent [`Area`], [max_count](https://willowprotocol.org/specs/grouping-entries/#aoi_count), and [max_size](https://willowprotocol.org/specs/grouping-entries/#aoi_size).
45    ///
46    /// A [`max_count`](https://willowprotocol.org/specs/grouping-entries/#aoi_count) of [`u64::MAX`] indicates that there is no limit on the number of entries. A [`max_size`](https://willowprotocol.org/specs/grouping-entries/#aoi_size) of [`u64::MAX`] indicates that there is no limit on the total [payload_lengths](https://willowprotocol.org/specs/data-model/index.html#entry_payload_length) of the entries.
47    ///
48    /// ```
49    /// use willow_data_model::prelude::*;
50    ///
51    /// let aoi = AreaOfInterest::new(
52    ///     Area::<2, 2, 2, u8>::new(None, Path::new(), TimeRange::new_closed(0.into(), 17.into())),
53    ///     NonZeroU64::new(5).unwrap(),
54    ///     400,
55    /// );
56    ///
57    /// assert_eq!(
58    ///     aoi.area(),
59    ///     &Area::<2, 2, 2, u8>::new(None, Path::new(), TimeRange::new_closed(0.into(), 17.into())),
60    /// );
61    /// assert_eq!(aoi.max_count(), NonZeroU64::new(5).unwrap());
62    /// assert_eq!(aoi.max_size(), 400);
63    /// ```
64    pub fn new(area: Area<MCL, MCC, MPL, S>, max_count: NonZeroU64, max_size: u64) -> Self {
65        Self {
66            area,
67            max_count,
68            max_size,
69        }
70    }
71
72    /// Returns a reference to the inner [`Area`].
73    ///
74    /// ```
75    /// use willow_data_model::prelude::*;
76    ///
77    /// let aoi = AreaOfInterest::new(
78    ///     Area::<2, 2, 2, u8>::new(None, Path::new(), TimeRange::new_closed(0.into(), 17.into())),
79    ///     NonZeroU64::new(5).unwrap(),
80    ///     400,
81    /// );
82    /// assert_eq!(aoi.area(), &Area::<2, 2, 2, u8>::new(None, Path::new(), TimeRange::new_closed(0.into(), 17.into())));
83    /// ```
84    ///
85    /// [Definition](https://willowprotocol.org/specs/grouping-entries/#aoi_area).
86    pub fn area(&self) -> &Area<MCL, MCC, MPL, S> {
87        &self.area
88    }
89
90    /// Returns the [max_count](https://willowprotocol.org/specs/grouping-entries/#aoi_count) of entries.
91    ///
92    /// A [`max_count`](https://willowprotocol.org/specs/grouping-entries/#aoi_count) of [`u64::MAX`] indicates that there is no limit on the number of entries in this area of interest.
93    ///
94    /// ```
95    /// use willow_data_model::prelude::*;
96    ///
97    /// let aoi = AreaOfInterest::new(
98    ///     Area::<2, 2, 2, u8>::new(None, Path::new(), TimeRange::new_closed(0.into(), 17.into())),
99    ///     NonZeroU64::new(5).unwrap(),
100    ///     400,
101    /// );
102    /// assert_eq!(aoi.max_count(), NonZeroU64::new(5).unwrap());
103    /// ```
104    ///
105    /// [Definition](https://willowprotocol.org/specs/grouping-entries/#AreaPath).
106    pub fn max_count(&self) -> NonZeroU64 {
107        self.max_count
108    }
109
110    /// Returns the [max_count](https://willowprotocol.org/specs/grouping-entries/#aoi_count) of entries.
111    ///
112    /// A [`max_size`](https://willowprotocol.org/specs/grouping-entries/#aoi_size) of [`u64::MAX`] indicates that there is no limit on the total [payload_lengths](https://willowprotocol.org/specs/data-model/index.html#entry_payload_length) of the entries in this area of interest.
113    ///
114    /// ```
115    /// use willow_data_model::prelude::*;
116    ///
117    /// let aoi = AreaOfInterest::new(
118    ///     Area::<2, 2, 2, u8>::new(None, Path::new(), TimeRange::new_closed(0.into(), 17.into())),
119    ///     NonZeroU64::new(5).unwrap(),
120    ///     400,
121    /// );
122    /// assert_eq!(aoi.max_size(), 400);
123    /// ```
124    ///
125    /// [Definition](https://willowprotocol.org/specs/grouping-entries/#AreaPath).
126    pub fn max_size(&self) -> u64 {
127        self.max_size
128    }
129
130    /// Sets the inner area.
131    pub fn set_area(&mut self, new_area: Area<MCL, MCC, MPL, S>) {
132        self.area = new_area;
133    }
134
135    /// Sets the [`max_count`](https://willowprotocol.org/specs/grouping-entries/#aoi_count).
136    ///
137    /// A [`max_count`](https://willowprotocol.org/specs/grouping-entries/#aoi_count) of [`u64::MAX`] indicates that there is no limit on the number of entries in this area of interest.
138    pub fn set_max_count(&mut self, new_max_count: NonZeroU64) {
139        self.max_count = new_max_count;
140    }
141
142    /// Sets the [`max_size`](https://willowprotocol.org/specs/grouping-entries/#aoi_size).
143    ///
144    /// A [`max_size`](https://willowprotocol.org/specs/grouping-entries/#aoi_size) of [`u64::MAX`] indicates that there is no limit on the total [payload_lengths](https://willowprotocol.org/specs/data-model/index.html#entry_payload_length) of the entries in this area of interest.
145    pub fn set_max_size(&mut self, new_max_size: u64) {
146        self.max_size = new_max_size;
147    }
148}
149
150impl<const MCL: usize, const MCC: usize, const MPL: usize, S> AreaOfInterest<MCL, MCC, MPL, S>
151where
152    S: PartialEq + Clone,
153{
154    /// Returns the [intersection](https://willowprotocol.org/specs/grouping-entries/#aoi_intersection) of `self` and `other`.
155    ///
156    /// ```
157    /// use willow_data_model::prelude::*;
158    ///
159    /// let aoi1 = AreaOfInterest::new(
160    ///     Area::<2, 2, 2, u8>::new(None, Path::new(), TimeRange::new_closed(0.into(), 17.into())),
161    ///     NonZeroU64::new(5).unwrap(),
162    ///     400,
163    /// );
164    /// let aoi2 = AreaOfInterest::new(
165    ///     Area::<2, 2, 2, u8>::new(None, Path::new(), TimeRange::new_open(14.into())),
166    ///     NonZeroU64::new(12).unwrap(),
167    ///     32,
168    /// );
169    ///
170    /// assert_eq!(
171    ///     aoi1.intersection(&aoi2),
172    ///     Ok(AreaOfInterest::new(
173    ///         Area::<2, 2, 2, u8>::new(
174    ///             None,
175    ///             Path::new(),
176    ///             TimeRange::new_closed(14.into(), 17.into()),
177    ///         ),
178    ///         NonZeroU64::new(5).unwrap(),
179    ///         32,
180    ///     )),
181    /// );
182    /// ```
183    pub fn intersection(&self, other: &Self) -> Result<Self, EmptyGrouping> {
184        Ok(Self::new(
185            self.area().wdm_intersection(other.area())?,
186            core::cmp::min(self.max_count(), other.max_count()),
187            self.max_size().greatest_lower_bound(&other.max_size()),
188        ))
189    }
190}
191
192/// An area is less than another iff is is empty but the other is not, or if its area is less than the other's area and its [`max_count`](https://willowprotocol.org/specs/grouping-entries/#aoi_count) and [`max_size`](https://willowprotocol.org/specs/grouping-entries/#aoi_size) are not strictly greater than those of the other.
193///
194/// This implementation assumes that `S` is inhabited by more than one value.
195impl<const MCL: usize, const MCC: usize, const MPL: usize, S> PartialOrd<Self>
196    for AreaOfInterest<MCL, MCC, MPL, S>
197where
198    S: Clone + Ord + PredecessorExceptForLeast + SuccessorExceptForGreatest,
199{
200    /// An area is less than another iff it has a [`max_count`](https://willowprotocol.org/specs/grouping-entries/#aoi_count) of zero (and the other doesn't), or if its area is less than the other's area and its [`max_count`](https://willowprotocol.org/specs/grouping-entries/#aoi_count) and [`max_size`](https://willowprotocol.org/specs/grouping-entries/#aoi_size) are not strictly greater than those of the other.
201    ///
202    /// This implementation assumes that `S` is inhabited by more than one value.
203    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
204        match (
205            self.area().partial_cmp(other.area())?,
206            self.max_count().cmp(&other.max_count()),
207            self.max_size().cmp(&other.max_size()),
208        ) {
209            (Ordering::Equal, Ordering::Equal, Ordering::Equal) => Some(Ordering::Equal),
210            (area_cmp, count_cmp, size_cmp) => {
211                if area_cmp.is_le() && count_cmp.is_le() && size_cmp.is_le() {
212                    Some(Ordering::Less)
213                } else if area_cmp.is_ge() && count_cmp.is_ge() && size_cmp.is_ge() {
214                    Some(Ordering::Greater)
215                } else {
216                    None
217                }
218            }
219        }
220    }
221}
222
223impl<const MCL: usize, const MCC: usize, const MPL: usize, S> GreatestElement
224    for AreaOfInterest<MCL, MCC, MPL, S>
225where
226    S: Clone + Ord + PredecessorExceptForLeast + SuccessorExceptForGreatest,
227{
228    fn greatest() -> Self {
229        Self::new(Area::greatest(), NonZeroU64::MAX, u64::MAX)
230    }
231
232    fn is_greatest(&self) -> bool {
233        self.area().is_greatest()
234            && self.max_count() == NonZeroU64::MAX
235            && self.max_size() == u64::MAX
236    }
237}