1use core::fmt::Debug;
2use core::hash::Hash;
3use core::{cmp::Ordering, ops::RangeBounds};
4
5#[cfg(feature = "dev")]
6use arbitrary::Arbitrary;
7
8use order_theory::{
9 GreatestElement, LeastElement, LowerSemilattice, PredecessorExceptForLeast,
10 SuccessorExceptForGreatest, UpperSemilattice,
11};
12
13use crate::prelude::*;
14
15#[derive(Clone, Debug)]
40#[cfg_attr(feature = "dev", derive(Arbitrary))]
41pub struct Area<const MCL: usize, const MCC: usize, const MPL: usize, S> {
42 subspace: Option<S>,
43 path: Path<MCL, MCC, MPL>,
44 times: TimeRange,
45}
46
47impl<const MCL: usize, const MCC: usize, const MPL: usize, S> Area<MCL, MCC, MPL, S> {
48 pub fn new<TR>(subspace: Option<S>, path: Path<MCL, MCC, MPL>, times: TR) -> Self
59 where
60 TR: Into<TimeRange>,
61 {
62 Self {
63 subspace,
64 path,
65 times: times.into(),
66 }
67 }
68
69 pub fn subspace(&self) -> Option<&S> {
83 self.subspace.as_ref()
84 }
85
86 pub fn path(&self) -> &Path<MCL, MCC, MPL> {
97 &self.path
98 }
99
100 pub fn times(&self) -> &TimeRange {
111 &self.times
112 }
113
114 pub fn set_subspace(&mut self, new_subspace: Option<S>) {
116 self.subspace = new_subspace;
117 }
118
119 pub fn set_path(&mut self, new_path: Path<MCL, MCC, MPL>) {
121 self.path = new_path;
122 }
123
124 pub fn set_times<TR>(&mut self, new_range: TR)
126 where
127 TR: Into<TimeRange>,
128 {
129 self.times = new_range.into();
130 }
131
132 pub fn new_subspace(subspace_id: S) -> Self {
144 Self::new(Some(subspace_id), Path::new(), ..)
145 }
146}
147
148impl<const MCL: usize, const MCC: usize, const MPL: usize, S> Grouping<MCL, MCC, MPL, S>
149 for Area<MCL, MCC, MPL, S>
150where
151 S: Clone + Ord + PredecessorExceptForLeast + SuccessorExceptForGreatest,
152{
153 fn wdm_includes<Coord>(&self, coord: &Coord) -> bool
154 where
155 Coord: Coordinatelike<MCL, MCC, MPL, S> + ?Sized,
156 {
157 self.times().contains(&coord.wdm_timestamp())
158 && self
159 .subspace()
160 .map(|subspace_id| subspace_id == coord.wdm_subspace_id())
161 .unwrap_or(true)
162 && coord.wdm_path().is_prefixed_by(self.path())
163 }
164}
165
166impl<const MCL: usize, const MCC: usize, const MPL: usize, S> PartialEq<Self>
170 for Area<MCL, MCC, MPL, S>
171where
172 S: Clone + Ord + PredecessorExceptForLeast + SuccessorExceptForGreatest,
173{
174 fn eq(&self, other: &Self) -> bool {
175 match (self.wdm_is_empty(), other.wdm_is_empty()) {
176 (true, true) => true,
177 (true, false) | (false, true) => false,
178 (false, false) => {
179 self.subspace() == other.subspace()
180 && self.path() == other.path()
181 && self.times() == other.times()
182 }
183 }
184 }
185}
186
187impl<const MCL: usize, const MCC: usize, const MPL: usize, S> Eq for Area<MCL, MCC, MPL, S> where
188 S: Clone + Ord + PredecessorExceptForLeast + SuccessorExceptForGreatest
189{
190}
191
192impl<const MCL: usize, const MCC: usize, const MPL: usize, S> PartialOrd<Self>
196 for Area<MCL, MCC, MPL, S>
197where
198 S: Clone + Ord + PredecessorExceptForLeast + SuccessorExceptForGreatest,
199{
200 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
204 match (self.wdm_is_empty(), other.wdm_is_empty()) {
205 (true, true) => Some(Ordering::Equal),
206 (true, false) => Some(Ordering::Less),
207 (false, true) => Some(Ordering::Greater),
208 (false, false) => {
209 match (
210 cmp_subspace(self.subspace(), other.subspace())?,
211 other.path().prefix_cmp(self.path())?,
212 self.times().partial_cmp(other.times())?,
213 ) {
214 (Ordering::Equal, Ordering::Equal, Ordering::Equal) => Some(Ordering::Equal),
215 (subspaces_cmp, paths_cmp, times_cmp) => {
216 if subspaces_cmp.is_le() && paths_cmp.is_le() && times_cmp.is_le() {
217 Some(Ordering::Less)
218 } else if subspaces_cmp.is_ge() && paths_cmp.is_ge() && times_cmp.is_ge() {
219 Some(Ordering::Greater)
220 } else {
221 None
222 }
223 }
224 }
225 }
226 }
227 }
228}
229
230fn cmp_subspace<S: PartialEq>(s1: Option<&S>, s2: Option<&S>) -> Option<Ordering> {
231 match (s1, s2) {
232 (None, None) => Some(Ordering::Equal),
233 (Some(_), None) => Some(Ordering::Less),
234 (None, Some(_)) => Some(Ordering::Greater),
235 (Some(s1), Some(s2)) => {
236 if s1 == s2 {
237 Some(Ordering::Equal)
238 } else {
239 None
240 }
241 }
242 }
243}
244
245impl<const MCL: usize, const MCC: usize, const MPL: usize, S> LeastElement
246 for Area<MCL, MCC, MPL, S>
247where
248 S: Clone + Ord + PredecessorExceptForLeast + SuccessorExceptForGreatest,
249{
250 fn least() -> Self {
251 Self::new(None, Path::new(), TimeRange::least())
252 }
253
254 fn is_least(&self) -> bool {
255 self.times().is_empty()
256 }
257}
258
259impl<const MCL: usize, const MCC: usize, const MPL: usize, S> GreatestElement
260 for Area<MCL, MCC, MPL, S>
261where
262 S: Clone + Ord + PredecessorExceptForLeast + SuccessorExceptForGreatest,
263{
264 fn greatest() -> Self {
265 Self::new(None, Path::new(), TimeRange::full())
266 }
267
268 fn is_greatest(&self) -> bool {
269 self.subspace().is_none() && self.times().is_full() && self.path().is_empty()
270 }
271}
272
273impl<const MCL: usize, const MCC: usize, const MPL: usize, S> LowerSemilattice
274 for Area<MCL, MCC, MPL, S>
275where
276 S: Clone + Ord + PredecessorExceptForLeast + SuccessorExceptForGreatest,
277{
278 fn greatest_lower_bound(&self, other: &Self) -> Self {
279 let subspace = match (self.subspace(), other.subspace()) {
280 (None, None) => None,
281 (Some(s), None) | (None, Some(s)) => Some(s.clone()),
282 (Some(s1), Some(s2)) => {
283 if s1 == s2 {
284 Some(s1.clone())
285 } else {
286 return Self::least();
287 }
288 }
289 };
290
291 let path = match self.path().prefix_cmp(other.path()) {
292 Some(Ordering::Greater) | Some(Ordering::Equal) => self.path().clone(),
293 Some(Ordering::Less) => other.path().clone(),
294 None => return Self::least(),
295 };
296
297 Self::new(
298 subspace,
299 path,
300 self.times().greatest_lower_bound(other.times()),
301 )
302 }
303}
304
305impl<const MCL: usize, const MCC: usize, const MPL: usize, S> UpperSemilattice
306 for Area<MCL, MCC, MPL, S>
307where
308 S: Clone + Ord + PredecessorExceptForLeast + SuccessorExceptForGreatest,
309{
310 fn least_upper_bound(&self, other: &Self) -> Self {
311 if self.wdm_is_empty() {
312 other.clone()
313 } else if other.wdm_is_empty() {
314 self.clone()
315 } else {
316 let subspace = match (self.subspace(), other.subspace()) {
317 (None, None) => None,
318 (Some(_), None) | (None, Some(_)) => None,
319 (Some(s1), Some(s2)) => {
320 if s1 == s2 {
321 Some(s1.clone())
322 } else {
323 None
324 }
325 }
326 };
327
328 let path = self.path().longest_common_prefix(other.path());
329
330 Self::new(
331 subspace,
332 path,
333 self.times().least_upper_bound(other.times()),
334 )
335 }
336 }
337}
338
339impl<const MCL: usize, const MCC: usize, const MPL: usize, S> Area<MCL, MCC, MPL, S>
340where
341 S: Ord + PredecessorExceptForLeast + SuccessorExceptForGreatest,
342{
343 pub fn admits_pruning_by<Coord>(&self, coord: &Coord) -> bool
354 where
355 Coord: Coordinatelike<MCL, MCC, MPL, S>,
356 {
357 if let Some(s) = self.subspace() {
358 if s != coord.wdm_subspace_id() {
359 return false;
360 }
361 }
362
363 if coord.wdm_timestamp() < *self.times().start() {
364 return false;
365 }
366
367 coord.wdm_path().is_related_to(self.path())
368 }
369}
370
371impl<const MCL: usize, const MCC: usize, const MPL: usize, S> Area<MCL, MCC, MPL, S>
372where
373 S: Clone + Ord + PredecessorExceptForLeast + SuccessorExceptForGreatest,
374{
375 pub fn full() -> Self {
386 Self::greatest()
387 }
388
389 pub fn is_full(&self) -> bool {
398 self.is_greatest()
399 }
400}
401
402impl<const MCL: usize, const MCC: usize, const MPL: usize, S> Hash for Area<MCL, MCC, MPL, S>
403where
404 S: Hash + Clone + Ord + PredecessorExceptForLeast + SuccessorExceptForGreatest,
405{
406 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
407 if self.is_least() {
408 255u8.hash(state);
409 } else if self.is_greatest() {
410 254u8.hash(state);
411 } else {
412 253u8.hash(state);
413 self.subspace.hash(state);
414 self.path.hash(state);
415 self.times.hash(state);
416 }
417 }
418}