1use core::cmp::min;
2use core::fmt::Debug;
3use core::hash::Hash;
4use core::{cmp::Ordering, ops::RangeBounds};
5
6#[cfg(feature = "dev")]
7use arbitrary::Arbitrary;
8
9use order_theory::{GreatestElement, UpperSemilattice};
10
11use compact_u64::*;
12use ufotofu::codec_prelude::*;
13
14use crate::is_bitflagged;
15use crate::paths::path_extends_path::*;
16use crate::prelude::*;
17
18#[derive(Clone, Debug, PartialEq, Eq, Hash)]
43#[cfg_attr(feature = "dev", derive(Arbitrary))]
44pub struct Area<const MCL: usize, const MCC: usize, const MPL: usize, S> {
45 subspace: Option<S>,
46 path: Path<MCL, MCC, MPL>,
47 times: TimeRange,
48}
49
50impl<const MCL: usize, const MCC: usize, const MPL: usize, S> Area<MCL, MCC, MPL, S> {
51 pub fn new(subspace: Option<S>, path: Path<MCL, MCC, MPL>, times: TimeRange) -> Self {
62 Self {
63 subspace,
64 path,
65 times,
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_area(subspace_id: S) -> Self {
144 Self::new(Some(subspace_id), Path::new(), TimeRange::full())
145 }
146
147 pub fn as_ref_subspace(&self) -> Area<MCL, MCC, MPL, &S> {
151 Area::new(self.subspace.as_ref(), self.path.clone(), self.times)
152 }
153}
154
155impl<const MCL: usize, const MCC: usize, const MPL: usize, S> Grouping<MCL, MCC, MPL, S>
156 for Area<MCL, MCC, MPL, S>
157where
158 S: PartialEq,
159{
160 fn wdm_includes<Coord>(&self, coord: &Coord) -> bool
161 where
162 Coord: Coordinatelike<MCL, MCC, MPL, S> + ?Sized,
163 {
164 self.times().contains(&coord.wdm_timestamp())
165 && self
166 .subspace()
167 .map(|subspace_id| subspace_id == coord.wdm_subspace_id())
168 .unwrap_or(true)
169 && coord.wdm_path().is_prefixed_by(self.path())
170 }
171
172 fn wdm_intersection(&self, other: &Self) -> Result<Self, EmptyGrouping>
173 where
174 S: Clone,
175 {
176 if self.path.is_related_to(other.path()) {
177 let times = self.times().intersection_willow_range(other.times())?;
178 let path = self.path().least_upper_bound(other.path());
179
180 match (self.subspace(), other.subspace()) {
181 (None, None) => Ok(Self::new(None, path, times)),
182 (Some(subspace), None) | (None, Some(subspace)) => {
183 Ok(Area::new(Some(subspace.clone()), path, times))
184 }
185 (Some(self_subspace), Some(other_subspace)) => {
186 if self_subspace == other_subspace {
187 Ok(Area::new(Some(self_subspace.clone()), path, times))
188 } else {
189 Err(EmptyGrouping)
190 }
191 }
192 }
193 } else {
194 Err(EmptyGrouping)
195 }
196 }
197}
198
199impl<const MCL: usize, const MCC: usize, const MPL: usize, S> PartialOrd<Self>
203 for Area<MCL, MCC, MPL, S>
204where
205 S: PartialEq,
206{
207 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
211 match (
212 cmp_subspace(self.subspace(), other.subspace())?,
213 other.path().prefix_cmp(self.path())?,
214 self.times().partial_cmp(other.times())?,
215 ) {
216 (Ordering::Equal, Ordering::Equal, Ordering::Equal) => Some(Ordering::Equal),
217 (subspaces_cmp, paths_cmp, times_cmp) => {
218 if subspaces_cmp.is_le() && paths_cmp.is_le() && times_cmp.is_le() {
219 Some(Ordering::Less)
220 } else if subspaces_cmp.is_ge() && paths_cmp.is_ge() && times_cmp.is_ge() {
221 Some(Ordering::Greater)
222 } else {
223 None
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> GreatestElement
246 for Area<MCL, MCC, MPL, S>
247where
248 S: PartialOrd,
249{
250 fn greatest() -> Self {
251 Self::new(None, Path::new(), TimeRange::full())
252 }
253
254 fn is_greatest(&self) -> bool {
255 self.subspace().is_none() && self.times().is_full() && self.path().is_empty()
256 }
257}
258
259impl<const MCL: usize, const MCC: usize, const MPL: usize, S> Area<MCL, MCC, MPL, S>
260where
261 S: PartialEq,
262{
263 pub fn admits_pruning_by<Coord>(&self, coord: &Coord) -> bool
274 where
275 Coord: Coordinatelike<MCL, MCC, MPL, S>,
276 {
277 if let Some(s) = self.subspace() {
278 if s != coord.wdm_subspace_id() {
279 return false;
280 }
281 }
282
283 if coord.wdm_timestamp() < *self.times().start() {
284 return false;
285 }
286
287 coord.wdm_path().is_related_to(self.path())
288 }
289}
290
291impl<const MCL: usize, const MCC: usize, const MPL: usize, S> Area<MCL, MCC, MPL, S> {
292 pub fn full() -> Self {
303 Self {
304 subspace: None,
305 path: Path::new(),
306 times: WillowRange::full(),
307 }
308 }
309
310 pub fn is_full(&self) -> bool {
319 self.subspace().is_none() && self.path.is_empty() && self.times.is_full()
320 }
321}
322
323impl<const MCL: usize, const MCC: usize, const MPL: usize, S>
329 RelativeEncodable<Area<MCL, MCC, MPL, S>> for Area<MCL, MCC, MPL, S>
330where
331 S: PartialEq + Encodable,
332{
333 async fn relative_encode<C>(
334 &self,
335 rel: &Area<MCL, MCC, MPL, S>,
336 consumer: &mut C,
337 ) -> Result<(), C::Error>
338 where
339 C: BulkConsumer<Item = u8> + ?Sized,
340 {
341 debug_assert!(self.can_be_encoded_relative_to(rel));
342
343 let self_times_start = u64::from(*self.times().start());
344 let self_times_end = self.times().end().map(|t| u64::from(*t));
345 let rel_times_start = u64::from(*rel.times().start());
346 let rel_times_end = rel.times().end().map(|t| u64::from(*t));
347
348 let (start_diff, start_from_start) = match rel_times_end {
349 None => (self_times_start - rel_times_start, true),
350 Some(rel_times_end) => {
351 if self_times_start - rel_times_start < rel_times_end - self_times_start {
352 (self_times_start - rel_times_start, true)
353 } else {
354 (rel_times_end - self_times_start, false)
355 }
356 }
357 };
358
359 let (end_diff, end_from_start) = match rel_times_end {
360 None => match self.times.end() {
361 None => (None, false),
362 Some(self_times_end) => (Some(u64::from(*self_times_end) - rel_times_start), true),
363 },
364
365 Some(rel_times_end) => {
366 let self_times_end: u64 = self_times_end.unwrap();
368
369 if self_times_end - rel_times_start < rel_times_end - self_times_end {
370 (Some(self_times_end - rel_times_start), true)
371 } else {
372 (Some(rel_times_end - self_times_end), false)
373 }
374 }
375 };
376
377 let mut header = 0;
378
379 if self.subspace() != rel.subspace() {
380 header |= 0b1000_0000;
381 }
382
383 if self.times().is_open() {
384 header |= 0b0100_0000;
385 }
386
387 if start_from_start {
388 header |= 0b0010_0000;
389 }
390
391 if self.times().is_closed() && end_from_start {
392 header |= 0b0001_0000;
393 }
394
395 write_tag(&mut header, 2, 4, start_diff);
396 write_tag(&mut header, 2, 6, end_diff.unwrap_or(0));
397
398 consumer.consume_item(header).await?;
399
400 if let (Some(self_subspace_id), None) = (self.subspace(), rel.subspace()) {
401 consumer.consume_encoded(self_subspace_id).await?;
402 }
403
404 cu64_encode(start_diff, 2, consumer).await?;
405
406 if let Some(end_diff) = end_diff {
407 cu64_encode(end_diff, 2, consumer).await?;
408 }
409
410 encode_path_extends_path(self.path(), rel.path(), consumer).await?;
411
412 Ok(())
413 }
414
415 fn can_be_encoded_relative_to(&self, rel: &Area<MCL, MCC, MPL, S>) -> bool {
417 rel.wdm_includes_grouping(self)
418 }
419}
420
421impl<const MCL: usize, const MCC: usize, const MPL: usize, S>
423 RelativeDecodable<Area<MCL, MCC, MPL, S>> for Area<MCL, MCC, MPL, S>
424where
425 S: DecodableCanonic<ErrorReason: Into<Blame>, ErrorCanonic: Into<Blame>> + Clone + PartialEq,
426{
427 type ErrorReason = Blame;
428
429 async fn relative_decode<P>(
430 rel: &Area<MCL, MCC, MPL, S>,
431 producer: &mut P,
432 ) -> Result<Self, DecodeError<P::Final, P::Error, Blame>>
433 where
434 P: BulkProducer<Item = u8> + ?Sized,
435 {
436 relative_decode_maybe_canonic::<false, MCL, MCC, MPL, S, P>(rel, producer).await
437 }
438}
439
440impl<const MCL: usize, const MCC: usize, const MPL: usize, S>
442 RelativeDecodableCanonic<Area<MCL, MCC, MPL, S>> for Area<MCL, MCC, MPL, S>
443where
444 S: DecodableCanonic<ErrorReason: Into<Blame>, ErrorCanonic: Into<Blame>> + Clone + PartialEq,
445{
446 type ErrorCanonic = Blame;
447
448 async fn relative_decode_canonic<P>(
449 rel: &Area<MCL, MCC, MPL, S>,
450 producer: &mut P,
451 ) -> Result<Self, DecodeError<P::Final, P::Error, Blame>>
452 where
453 P: BulkProducer<Item = u8> + ?Sized,
454 Self: Sized,
455 {
456 relative_decode_maybe_canonic::<true, MCL, MCC, MPL, S, P>(rel, producer).await
457 }
458}
459
460impl<const MCL: usize, const MCC: usize, const MPL: usize, S>
462 RelativeEncodableKnownLength<Area<MCL, MCC, MPL, S>> for Area<MCL, MCC, MPL, S>
463where
464 S: PartialEq + EncodableKnownLength,
465{
466 fn len_of_relative_encoding(&self, rel: &Area<MCL, MCC, MPL, S>) -> usize {
467 let mut encoding_len = 1; let self_times_start = u64::from(*self.times().start());
470 let self_times_end = self.times().end().map(|t| u64::from(*t));
471 let rel_times_start = u64::from(*rel.times().start());
472 let rel_times_end = rel.times().end().map(|t| u64::from(*t));
473
474 let start_diff = match rel_times_end {
475 None => self_times_start - rel_times_start,
476 Some(rel_times_end) => min(
477 self_times_start - rel_times_start,
478 rel_times_end - self_times_start,
479 ),
480 };
481
482 let end_diff = match rel_times_end {
483 None => self_times_end.map(|self_times_end| self_times_end - rel_times_start),
484 Some(rel_times_end) => {
485 let self_times_end: u64 = self_times_end.unwrap();
487 Some(min(
488 self_times_end - rel_times_start,
489 rel_times_end - self_times_end,
490 ))
491 }
492 };
493
494 if let (Some(self_subspace_id), None) = (self.subspace(), rel.subspace()) {
495 encoding_len += self_subspace_id.len_of_encoding();
496 }
497
498 encoding_len += cu64_len_of_encoding(2, start_diff);
499
500 if let Some(end_diff) = end_diff {
501 encoding_len += cu64_len_of_encoding(2, end_diff);
502 }
503
504 encoding_len += path_extends_path_encoding_len(self.path(), rel.path());
505
506 encoding_len
507 }
508}
509
510async fn relative_decode_maybe_canonic<
511 const CANONIC: bool,
512 const MCL: usize,
513 const MCC: usize,
514 const MPL: usize,
515 S,
516 P,
517>(
518 rel: &Area<MCL, MCC, MPL, S>,
519 producer: &mut P,
520) -> Result<Area<MCL, MCC, MPL, S>, DecodeError<P::Final, P::Error, Blame>>
521where
522 P: BulkProducer<Item = u8> + ?Sized,
523 S: DecodableCanonic<ErrorReason: Into<Blame>, ErrorCanonic: Into<Blame>> + Clone + PartialEq,
524{
525 let header = producer.produce_item().await?;
526
527 let is_subspace_encoded = is_bitflagged(header, 0);
529
530 let is_times_end_open = is_bitflagged(header, 1);
532
533 let start_from_start = is_bitflagged(header, 2);
535
536 let end_from_start = is_bitflagged(header, 3);
538
539 if CANONIC && is_times_end_open && (is_bitflagged(header, 6) || is_bitflagged(header, 7)) {
542 return Err(DecodeError::Other(Blame::TheirFault));
543 }
544 let subspace = if is_subspace_encoded {
547 let subspace_id = producer
548 .produce_decoded_canonic()
549 .await
550 .map_err(|err| err.map_other(Into::into))?;
551
552 if let Some(rel_subspace_id) = rel.subspace() {
553 if &subspace_id != rel_subspace_id {
554 return Err(DecodeError::Other(Blame::TheirFault));
556 } else if CANONIC {
557 return Err(DecodeError::Other(Blame::TheirFault));
559 }
560 }
561
562 Some(subspace_id)
563 } else {
564 rel.subspace.clone()
565 };
566
567 let start_diff = if CANONIC {
568 cu64_decode_canonic(header, 2, 4, producer)
569 .await
570 .map_err(|err| err.map_other(|_| Blame::TheirFault))?
571 } else {
572 cu64_decode(header, 2, 4, producer)
573 .await
574 .map_err(|err| err.map_other(|_| Blame::TheirFault))?
575 };
576
577 let rel_times_start = u64::from(*rel.times().start());
578 let rel_times_end = rel.times().end().map(|t| u64::from(*t));
579
580 let start = if start_from_start {
581 rel_times_start
582 .checked_add(start_diff)
583 .ok_or(DecodeError::Other(Blame::TheirFault))?
584 } else {
585 match rel_times_end {
586 None => {
587 return Err(DecodeError::Other(Blame::TheirFault));
589 }
590 Some(rel_times_end) => rel_times_end
591 .checked_sub(start_diff)
592 .ok_or(DecodeError::Other(Blame::TheirFault))?,
593 }
594 };
595
596 if CANONIC {
597 let should_have_set_start_from_start = match rel_times_end {
598 None => true,
599 Some(rel_times_end) => {
600 let start_diff = start
601 .checked_sub(rel_times_start)
602 .ok_or(DecodeError::Other(Blame::TheirFault))?;
603 let end_diff = rel_times_end
604 .checked_sub(start)
605 .ok_or(DecodeError::Other(Blame::TheirFault))?;
606 start_diff < end_diff
607 }
608 };
609
610 if start_from_start != should_have_set_start_from_start {
611 return Err(DecodeError::Other(Blame::TheirFault));
612 }
613 }
614
615 let end = if is_times_end_open {
616 if end_from_start {
617 return Err(DecodeError::Other(Blame::TheirFault));
618 }
619
620 None
621 } else {
622 let end_diff = if CANONIC {
623 cu64_decode_canonic(header, 2, 6, producer)
624 .await
625 .map_err(|err| err.map_other(|_| Blame::TheirFault))?
626 } else {
627 cu64_decode(header, 2, 6, producer)
628 .await
629 .map_err(|err| err.map_other(|_| Blame::TheirFault))?
630 };
631
632 let end = if end_from_start {
633 rel_times_start
634 .checked_add(end_diff)
635 .ok_or(DecodeError::Other(Blame::TheirFault))?
636 } else {
637 match rel_times_end {
638 None => {
639 return Err(DecodeError::Other(Blame::TheirFault));
641 }
642 Some(rel_times_end) => rel_times_end
643 .checked_sub(end_diff)
644 .ok_or(DecodeError::Other(Blame::TheirFault))?,
645 }
646 };
647
648 if CANONIC {
649 let should_have_set_end_from_start = match rel_times_end {
650 None => true,
651 Some(rel_times_end) => {
652 let start_diff = end
653 .checked_sub(rel_times_start)
654 .ok_or(DecodeError::Other(Blame::TheirFault))?;
655 let end_diff = rel_times_end
656 .checked_sub(end)
657 .ok_or(DecodeError::Other(Blame::TheirFault))?;
658 start_diff < end_diff
659 }
660 };
661
662 if end_from_start != should_have_set_end_from_start {
663 return Err(DecodeError::Other(Blame::TheirFault));
664 }
665 }
666
667 Some(end)
668 };
669
670 let times = WillowRange::try_new(Timestamp::from(start), end.map(Timestamp::from))
671 .map_err(|_| DecodeError::Other(Blame::TheirFault))?;
672
673 let path = if CANONIC {
674 decode_path_extends_path_canonic(rel.path(), producer)
675 .await
676 .map_err(|err| err.map_other(|_| Blame::TheirFault))?
677 } else {
678 decode_path_extends_path(rel.path(), producer)
679 .await
680 .map_err(|err| err.map_other(|_| Blame::TheirFault))?
681 };
682
683 if !rel.times().includes_willow_range(×) {
684 return Err(DecodeError::Other(Blame::TheirFault));
685 }
686
687 Ok(Area::new(subspace, path, times))
688}
689
690#[cfg(feature = "dev")]
692pub fn arbitrary_area_in_area<'a, const MCL: usize, const MCC: usize, const MPL: usize, S>(
693 reference: &Area<MCL, MCC, MPL, S>,
694 u: &mut arbitrary::Unstructured<'a>,
695) -> arbitrary::Result<Area<MCL, MCC, MPL, S>>
696where
697 S: Arbitrary<'a> + Clone,
698{
699 let subspace = match reference.subspace() {
700 None => Option::<S>::arbitrary(u)?,
701 Some(s) => Some(s.clone()),
702 };
703
704 let path_suffix = Path::<MCL, MCC, MPL>::arbitrary(u)?;
705 let path = reference
706 .path()
707 .append_path(&path_suffix)
708 .unwrap_or_else(|_| reference.path().clone());
709
710 let times_candidate = TimeRange::arbitrary(u)?;
711 let times = if reference.times().includes_willow_range(×_candidate) {
712 times_candidate
713 } else {
714 *reference.times()
715 };
716
717 Ok(Area {
718 subspace,
719 path,
720 times,
721 })
722}