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}