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