1use crate::{Epochable, Viewable};
33use bytes::{Buf, BufMut};
34use commonware_codec::{varint::UInt, EncodeSize, Error, Read, ReadExt, Write};
35use commonware_utils::sequence::U64;
36use std::{
37 fmt::{self, Display, Formatter},
38 marker::PhantomData,
39 num::NonZeroU64,
40};
41
42#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
47#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
48pub struct Epoch(u64);
49
50impl Epoch {
51 pub const fn zero() -> Self {
53 Self(0)
54 }
55
56 pub const fn new(value: u64) -> Self {
58 Self(value)
59 }
60
61 pub const fn get(self) -> u64 {
63 self.0
64 }
65
66 pub const fn is_zero(self) -> bool {
68 self.0 == 0
69 }
70
71 pub const fn next(self) -> Self {
78 Self(self.0.checked_add(1).expect("epoch overflow"))
79 }
80
81 pub fn previous(self) -> Option<Self> {
87 self.0.checked_sub(1).map(Self)
88 }
89
90 pub const fn saturating_add(self, delta: EpochDelta) -> Self {
92 Self(self.0.saturating_add(delta.0))
93 }
94
95 pub fn checked_sub(self, delta: EpochDelta) -> Option<Self> {
97 self.0.checked_sub(delta.0).map(Self)
98 }
99
100 pub const fn saturating_sub(self, delta: EpochDelta) -> Self {
102 Self(self.0.saturating_sub(delta.0))
103 }
104}
105
106impl Display for Epoch {
107 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
108 write!(f, "{}", self.0)
109 }
110}
111
112impl Read for Epoch {
113 type Cfg = ();
114
115 fn read_cfg(buf: &mut impl Buf, _cfg: &Self::Cfg) -> Result<Self, Error> {
116 let value: u64 = UInt::read(buf)?.into();
117 Ok(Self(value))
118 }
119}
120
121impl Write for Epoch {
122 fn write(&self, buf: &mut impl BufMut) {
123 UInt(self.0).write(buf);
124 }
125}
126
127impl EncodeSize for Epoch {
128 fn encode_size(&self) -> usize {
129 UInt(self.0).encode_size()
130 }
131}
132
133impl From<Epoch> for U64 {
134 fn from(epoch: Epoch) -> Self {
135 Self::from(epoch.get())
136 }
137}
138
139#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
144#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
145pub struct View(u64);
146
147impl View {
148 pub const fn zero() -> Self {
150 Self(0)
151 }
152
153 pub const fn new(value: u64) -> Self {
155 Self(value)
156 }
157
158 pub const fn get(self) -> u64 {
160 self.0
161 }
162
163 pub const fn is_zero(self) -> bool {
165 self.0 == 0
166 }
167
168 pub const fn next(self) -> Self {
175 Self(self.0.checked_add(1).expect("view overflow"))
176 }
177
178 pub fn previous(self) -> Option<Self> {
184 self.0.checked_sub(1).map(Self)
185 }
186
187 pub const fn saturating_add(self, delta: ViewDelta) -> Self {
189 Self(self.0.saturating_add(delta.0))
190 }
191
192 pub const fn saturating_sub(self, delta: ViewDelta) -> Self {
194 Self(self.0.saturating_sub(delta.0))
195 }
196
197 pub const fn range(start: Self, end: Self) -> ViewRange {
201 ViewRange {
202 inner: start.get()..end.get(),
203 }
204 }
205}
206
207impl Display for View {
208 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
209 write!(f, "{}", self.0)
210 }
211}
212
213impl Read for View {
214 type Cfg = ();
215
216 fn read_cfg(buf: &mut impl Buf, _cfg: &Self::Cfg) -> Result<Self, Error> {
217 let value: u64 = UInt::read(buf)?.into();
218 Ok(Self(value))
219 }
220}
221
222impl Write for View {
223 fn write(&self, buf: &mut impl BufMut) {
224 UInt(self.0).write(buf);
225 }
226}
227
228impl EncodeSize for View {
229 fn encode_size(&self) -> usize {
230 UInt(self.0).encode_size()
231 }
232}
233
234impl From<View> for U64 {
235 fn from(view: View) -> Self {
236 Self::from(view.get())
237 }
238}
239
240#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
248pub struct Delta<T>(u64, PhantomData<T>);
249
250impl<T> Delta<T> {
251 pub const fn zero() -> Self {
253 Self(0, PhantomData)
254 }
255
256 pub const fn new(value: u64) -> Self {
258 Self(value, PhantomData)
259 }
260
261 pub const fn get(self) -> u64 {
263 self.0
264 }
265
266 pub const fn is_zero(self) -> bool {
268 self.0 == 0
269 }
270}
271
272impl<T> Display for Delta<T> {
273 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
274 write!(f, "{}", self.0)
275 }
276}
277
278pub type EpochDelta = Delta<Epoch>;
283
284pub type ViewDelta = Delta<View>;
289
290#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
295#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
296pub struct Round {
297 epoch: Epoch,
298 view: View,
299}
300
301impl Round {
302 pub const fn new(epoch: Epoch, view: View) -> Self {
304 Self { epoch, view }
305 }
306
307 pub const fn zero() -> Self {
309 Self::new(Epoch::zero(), View::zero())
310 }
311
312 pub const fn epoch(self) -> Epoch {
314 self.epoch
315 }
316
317 pub const fn view(self) -> View {
319 self.view
320 }
321}
322
323impl Epochable for Round {
324 fn epoch(&self) -> Epoch {
325 self.epoch
326 }
327}
328
329impl Viewable for Round {
330 fn view(&self) -> View {
331 self.view
332 }
333}
334
335impl From<(Epoch, View)> for Round {
336 fn from((epoch, view): (Epoch, View)) -> Self {
337 Self { epoch, view }
338 }
339}
340
341impl From<Round> for (Epoch, View) {
342 fn from(round: Round) -> Self {
343 (round.epoch, round.view)
344 }
345}
346
347#[derive(Clone, Copy, Debug, PartialEq, Eq)]
351pub enum EpochPhase {
352 Early,
354 Midpoint,
356 Late,
358}
359
360#[derive(Clone, Copy, Debug, PartialEq, Eq)]
362pub struct EpochInfo {
363 epoch: Epoch,
364 height: u64,
365 first: u64,
366 last: u64,
367}
368
369impl EpochInfo {
370 pub const fn new(epoch: Epoch, height: u64, first: u64, last: u64) -> Self {
372 Self {
373 epoch,
374 height,
375 first,
376 last,
377 }
378 }
379
380 pub const fn epoch(&self) -> Epoch {
382 self.epoch
383 }
384
385 pub const fn height(&self) -> u64 {
387 self.height
388 }
389
390 pub const fn first(&self) -> u64 {
392 self.first
393 }
394
395 pub const fn last(&self) -> u64 {
397 self.last
398 }
399
400 pub const fn length(&self) -> u64 {
402 self.last - self.first + 1
403 }
404
405 pub const fn relative(&self) -> u64 {
407 self.height - self.first
408 }
409
410 pub const fn phase(&self) -> EpochPhase {
412 let relative = self.relative();
413 let midpoint = self.length() / 2;
414
415 if relative < midpoint {
416 EpochPhase::Early
417 } else if relative == midpoint {
418 EpochPhase::Midpoint
419 } else {
420 EpochPhase::Late
421 }
422 }
423}
424
425pub trait Epocher: Clone + Send + Sync + 'static {
427 fn containing(&self, height: u64) -> Option<EpochInfo>;
431
432 fn first(&self, epoch: Epoch) -> Option<u64>;
436
437 fn last(&self, epoch: Epoch) -> Option<u64>;
441}
442
443#[derive(Clone, Debug, PartialEq, Eq)]
445pub struct FixedEpocher(u64);
446
447impl FixedEpocher {
448 pub const fn new(length: NonZeroU64) -> Self {
457 Self(length.get())
458 }
459
460 fn bounds(&self, epoch: Epoch) -> Option<(u64, u64)> {
463 let first = epoch.get().checked_mul(self.0)?;
464 let last = first.checked_add(self.0 - 1)?;
465 Some((first, last))
466 }
467}
468
469impl Epocher for FixedEpocher {
470 fn containing(&self, height: u64) -> Option<EpochInfo> {
471 let epoch = Epoch::new(height / self.0);
472 let (first, last) = self.bounds(epoch)?;
473 Some(EpochInfo::new(epoch, height, first, last))
474 }
475
476 fn first(&self, epoch: Epoch) -> Option<u64> {
477 self.bounds(epoch).map(|(first, _)| first)
478 }
479
480 fn last(&self, epoch: Epoch) -> Option<u64> {
481 self.bounds(epoch).map(|(_, last)| last)
482 }
483}
484
485impl Read for Round {
486 type Cfg = ();
487
488 fn read_cfg(buf: &mut impl Buf, _cfg: &Self::Cfg) -> Result<Self, Error> {
489 Ok(Self {
490 epoch: Epoch::read(buf)?,
491 view: View::read(buf)?,
492 })
493 }
494}
495
496impl Write for Round {
497 fn write(&self, buf: &mut impl BufMut) {
498 self.epoch.write(buf);
499 self.view.write(buf);
500 }
501}
502
503impl EncodeSize for Round {
504 fn encode_size(&self) -> usize {
505 self.epoch.encode_size() + self.view.encode_size()
506 }
507}
508
509impl Display for Round {
510 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
511 write!(f, "({}, {})", self.epoch, self.view)
512 }
513}
514
515pub struct ViewRange {
519 inner: std::ops::Range<u64>,
520}
521
522impl Iterator for ViewRange {
523 type Item = View;
524
525 fn next(&mut self) -> Option<Self::Item> {
526 self.inner.next().map(View::new)
527 }
528
529 fn size_hint(&self) -> (usize, Option<usize>) {
530 self.inner.size_hint()
531 }
532}
533
534impl DoubleEndedIterator for ViewRange {
535 fn next_back(&mut self) -> Option<Self::Item> {
536 self.inner.next_back().map(View::new)
537 }
538}
539
540impl ExactSizeIterator for ViewRange {
541 fn len(&self) -> usize {
542 self.size_hint().0
543 }
544}
545
546#[cfg(test)]
547mod tests {
548 use super::*;
549 use commonware_codec::{DecodeExt, Encode, EncodeSize};
550 use commonware_utils::NZU64;
551
552 #[test]
553 fn test_epoch_constructors() {
554 assert_eq!(Epoch::zero().get(), 0);
555 assert_eq!(Epoch::new(42).get(), 42);
556 assert_eq!(Epoch::default().get(), 0);
557 }
558
559 #[test]
560 fn test_epoch_is_zero() {
561 assert!(Epoch::zero().is_zero());
562 assert!(Epoch::new(0).is_zero());
563 assert!(!Epoch::new(1).is_zero());
564 assert!(!Epoch::new(100).is_zero());
565 }
566
567 #[test]
568 fn test_epoch_next() {
569 assert_eq!(Epoch::zero().next().get(), 1);
570 assert_eq!(Epoch::new(5).next().get(), 6);
571 assert_eq!(Epoch::new(999).next().get(), 1000);
572 }
573
574 #[test]
575 #[should_panic(expected = "epoch overflow")]
576 fn test_epoch_next_overflow() {
577 Epoch::new(u64::MAX).next();
578 }
579
580 #[test]
581 fn test_epoch_previous() {
582 assert_eq!(Epoch::zero().previous(), None);
583 assert_eq!(Epoch::new(1).previous(), Some(Epoch::zero()));
584 assert_eq!(Epoch::new(5).previous(), Some(Epoch::new(4)));
585 assert_eq!(Epoch::new(1000).previous(), Some(Epoch::new(999)));
586 }
587
588 #[test]
589 fn test_epoch_saturating_add() {
590 assert_eq!(Epoch::zero().saturating_add(EpochDelta::new(5)).get(), 5);
591 assert_eq!(Epoch::new(10).saturating_add(EpochDelta::new(20)).get(), 30);
592 assert_eq!(
593 Epoch::new(u64::MAX)
594 .saturating_add(EpochDelta::new(1))
595 .get(),
596 u64::MAX
597 );
598 assert_eq!(
599 Epoch::new(u64::MAX - 5)
600 .saturating_add(EpochDelta::new(10))
601 .get(),
602 u64::MAX
603 );
604 }
605
606 #[test]
607 fn test_epoch_checked_sub() {
608 assert_eq!(
609 Epoch::new(10).checked_sub(EpochDelta::new(5)),
610 Some(Epoch::new(5))
611 );
612 assert_eq!(
613 Epoch::new(5).checked_sub(EpochDelta::new(5)),
614 Some(Epoch::zero())
615 );
616 assert_eq!(Epoch::new(5).checked_sub(EpochDelta::new(10)), None);
617 assert_eq!(Epoch::zero().checked_sub(EpochDelta::new(1)), None);
618 }
619
620 #[test]
621 fn test_epoch_saturating_sub() {
622 assert_eq!(Epoch::new(10).saturating_sub(EpochDelta::new(5)).get(), 5);
623 assert_eq!(Epoch::new(5).saturating_sub(EpochDelta::new(5)).get(), 0);
624 assert_eq!(Epoch::new(5).saturating_sub(EpochDelta::new(10)).get(), 0);
625 assert_eq!(Epoch::zero().saturating_sub(EpochDelta::new(100)).get(), 0);
626 }
627
628 #[test]
629 fn test_epoch_display() {
630 assert_eq!(format!("{}", Epoch::zero()), "0");
631 assert_eq!(format!("{}", Epoch::new(42)), "42");
632 assert_eq!(format!("{}", Epoch::new(1000)), "1000");
633 }
634
635 #[test]
636 fn test_epoch_ordering() {
637 assert!(Epoch::zero() < Epoch::new(1));
638 assert!(Epoch::new(5) < Epoch::new(10));
639 assert!(Epoch::new(10) > Epoch::new(5));
640 assert_eq!(Epoch::new(42), Epoch::new(42));
641 }
642
643 #[test]
644 fn test_epoch_encode_decode() {
645 let cases = vec![0u64, 1, 127, 128, 255, 256, u64::MAX];
646 for value in cases {
647 let epoch = Epoch::new(value);
648 let encoded = epoch.encode();
649 assert_eq!(encoded.len(), epoch.encode_size());
650 let decoded = Epoch::decode(encoded).unwrap();
651 assert_eq!(epoch, decoded);
652 }
653 }
654
655 #[test]
656 fn test_view_constructors() {
657 assert_eq!(View::zero().get(), 0);
658 assert_eq!(View::new(42).get(), 42);
659 assert_eq!(View::new(100).get(), 100);
660 assert_eq!(View::default().get(), 0);
661 }
662
663 #[test]
664 fn test_view_is_zero() {
665 assert!(View::zero().is_zero());
666 assert!(View::new(0).is_zero());
667 assert!(!View::new(1).is_zero());
668 assert!(!View::new(100).is_zero());
669 }
670
671 #[test]
672 fn test_view_next() {
673 assert_eq!(View::zero().next().get(), 1);
674 assert_eq!(View::new(5).next().get(), 6);
675 assert_eq!(View::new(999).next().get(), 1000);
676 }
677
678 #[test]
679 #[should_panic(expected = "view overflow")]
680 fn test_view_next_overflow() {
681 View::new(u64::MAX).next();
682 }
683
684 #[test]
685 fn test_view_previous() {
686 assert_eq!(View::zero().previous(), None);
687 assert_eq!(View::new(1).previous(), Some(View::zero()));
688 assert_eq!(View::new(5).previous(), Some(View::new(4)));
689 assert_eq!(View::new(1000).previous(), Some(View::new(999)));
690 }
691
692 #[test]
693 fn test_view_saturating_add() {
694 let delta5 = ViewDelta::new(5);
695 let delta100 = ViewDelta::new(100);
696 assert_eq!(View::zero().saturating_add(delta5).get(), 5);
697 assert_eq!(View::new(10).saturating_add(delta100).get(), 110);
698 assert_eq!(
699 View::new(u64::MAX).saturating_add(ViewDelta::new(1)).get(),
700 u64::MAX
701 );
702 }
703
704 #[test]
705 fn test_view_saturating_sub() {
706 let delta5 = ViewDelta::new(5);
707 let delta100 = ViewDelta::new(100);
708 assert_eq!(View::new(10).saturating_sub(delta5).get(), 5);
709 assert_eq!(View::new(5).saturating_sub(delta5).get(), 0);
710 assert_eq!(View::new(5).saturating_sub(delta100).get(), 0);
711 assert_eq!(View::zero().saturating_sub(delta100).get(), 0);
712 }
713
714 #[test]
715 fn test_view_display() {
716 assert_eq!(format!("{}", View::zero()), "0");
717 assert_eq!(format!("{}", View::new(42)), "42");
718 assert_eq!(format!("{}", View::new(1000)), "1000");
719 }
720
721 #[test]
722 fn test_view_ordering() {
723 assert!(View::zero() < View::new(1));
724 assert!(View::new(5) < View::new(10));
725 assert!(View::new(10) > View::new(5));
726 assert_eq!(View::new(42), View::new(42));
727 }
728
729 #[test]
730 fn test_view_encode_decode() {
731 let cases = vec![0u64, 1, 127, 128, 255, 256, u64::MAX];
732 for value in cases {
733 let view = View::new(value);
734 let encoded = view.encode();
735 assert_eq!(encoded.len(), view.encode_size());
736 let decoded = View::decode(encoded).unwrap();
737 assert_eq!(view, decoded);
738 }
739 }
740
741 #[test]
742 fn test_view_delta_constructors() {
743 assert_eq!(ViewDelta::zero().get(), 0);
744 assert_eq!(ViewDelta::new(42).get(), 42);
745 assert_eq!(ViewDelta::new(100).get(), 100);
746 assert_eq!(ViewDelta::default().get(), 0);
747 }
748
749 #[test]
750 fn test_view_delta_is_zero() {
751 assert!(ViewDelta::zero().is_zero());
752 assert!(ViewDelta::new(0).is_zero());
753 assert!(!ViewDelta::new(1).is_zero());
754 assert!(!ViewDelta::new(100).is_zero());
755 }
756
757 #[test]
758 fn test_view_delta_display() {
759 assert_eq!(format!("{}", ViewDelta::zero()), "0");
760 assert_eq!(format!("{}", ViewDelta::new(42)), "42");
761 assert_eq!(format!("{}", ViewDelta::new(1000)), "1000");
762 }
763
764 #[test]
765 fn test_view_delta_ordering() {
766 assert!(ViewDelta::zero() < ViewDelta::new(1));
767 assert!(ViewDelta::new(5) < ViewDelta::new(10));
768 assert!(ViewDelta::new(10) > ViewDelta::new(5));
769 assert_eq!(ViewDelta::new(42), ViewDelta::new(42));
770 }
771
772 #[test]
773 fn test_round_cmp() {
774 assert!(Round::new(Epoch::new(1), View::new(2)) < Round::new(Epoch::new(1), View::new(3)));
775 assert!(Round::new(Epoch::new(1), View::new(2)) < Round::new(Epoch::new(2), View::new(1)));
776 }
777
778 #[test]
779 fn test_round_encode_decode_roundtrip() {
780 let r: Round = (Epoch::new(42), View::new(1_000_000)).into();
781 let encoded = r.encode();
782 assert_eq!(encoded.len(), r.encode_size());
783 let decoded = Round::decode(encoded).unwrap();
784 assert_eq!(r, decoded);
785 }
786
787 #[test]
788 fn test_round_conversions() {
789 let r: Round = (Epoch::new(5), View::new(6)).into();
790 assert_eq!(r.epoch(), Epoch::new(5));
791 assert_eq!(r.view(), View::new(6));
792 let tuple: (Epoch, View) = r.into();
793 assert_eq!(tuple, (Epoch::new(5), View::new(6)));
794 }
795
796 #[test]
797 fn test_round_new() {
798 let r = Round::new(Epoch::new(10), View::new(20));
799 assert_eq!(r.epoch(), Epoch::new(10));
800 assert_eq!(r.view(), View::new(20));
801
802 let r2 = Round::new(Epoch::new(5), View::new(15));
803 assert_eq!(r2.epoch(), Epoch::new(5));
804 assert_eq!(r2.view(), View::new(15));
805 }
806
807 #[test]
808 fn test_round_display() {
809 let r = Round::new(Epoch::new(5), View::new(100));
810 assert_eq!(format!("{r}"), "(5, 100)");
811 }
812
813 #[test]
814 fn view_range_iterates() {
815 let collected: Vec<_> = View::range(View::new(3), View::new(6))
816 .map(View::get)
817 .collect();
818 assert_eq!(collected, vec![3, 4, 5]);
819 }
820
821 #[test]
822 fn view_range_empty() {
823 let collected: Vec<_> = View::range(View::new(5), View::new(5)).collect();
824 assert_eq!(collected, vec![]);
825
826 let collected: Vec<_> = View::range(View::new(10), View::new(5)).collect();
827 assert_eq!(collected, vec![]);
828 }
829
830 #[test]
831 fn view_range_single() {
832 let collected: Vec<_> = View::range(View::new(5), View::new(6))
833 .map(View::get)
834 .collect();
835 assert_eq!(collected, vec![5]);
836 }
837
838 #[test]
839 fn view_range_size_hint() {
840 let range = View::range(View::new(3), View::new(10));
841 assert_eq!(range.size_hint(), (7, Some(7)));
842 assert_eq!(range.len(), 7);
843
844 let empty = View::range(View::new(5), View::new(5));
845 assert_eq!(empty.size_hint(), (0, Some(0)));
846 assert_eq!(empty.len(), 0);
847 }
848
849 #[test]
850 fn view_range_collect() {
851 let views: Vec<View> = View::range(View::new(0), View::new(3)).collect();
852 assert_eq!(views, vec![View::zero(), View::new(1), View::new(2)]);
853 }
854
855 #[test]
856 fn view_range_iterator_next() {
857 let mut range = View::range(View::new(5), View::new(8));
858 assert_eq!(range.next(), Some(View::new(5)));
859 assert_eq!(range.next(), Some(View::new(6)));
860 assert_eq!(range.next(), Some(View::new(7)));
861 assert_eq!(range.next(), None);
862 assert_eq!(range.next(), None); }
864
865 #[test]
866 fn view_range_exact_size_iterator() {
867 let range = View::range(View::new(10), View::new(15));
868 assert_eq!(range.len(), 5);
869 assert_eq!(range.size_hint(), (5, Some(5)));
870
871 let mut range = View::range(View::new(10), View::new(15));
872 assert_eq!(range.len(), 5);
873 range.next();
874 assert_eq!(range.len(), 4);
875 range.next();
876 assert_eq!(range.len(), 3);
877 }
878
879 #[test]
880 fn view_range_rev() {
881 let collected: Vec<_> = View::range(View::new(3), View::new(7))
883 .rev()
884 .map(View::get)
885 .collect();
886 assert_eq!(collected, vec![6, 5, 4, 3]);
887 }
888
889 #[test]
890 fn view_range_double_ended() {
891 let mut range = View::range(View::new(5), View::new(10));
893 assert_eq!(range.next(), Some(View::new(5)));
894 assert_eq!(range.next_back(), Some(View::new(9)));
895 assert_eq!(range.next(), Some(View::new(6)));
896 assert_eq!(range.next_back(), Some(View::new(8)));
897 assert_eq!(range.len(), 1);
898 assert_eq!(range.next(), Some(View::new(7)));
899 assert_eq!(range.next(), None);
900 assert_eq!(range.next_back(), None);
901 }
902
903 #[test]
904 fn test_fixed_epoch_strategy() {
905 let epocher = FixedEpocher::new(NZU64!(100));
906
907 let bounds = epocher.containing(0).unwrap();
909 assert_eq!(bounds.epoch(), Epoch::new(0));
910 assert_eq!(bounds.first(), 0);
911 assert_eq!(bounds.last(), 99);
912 assert_eq!(bounds.length(), 100);
913
914 let bounds = epocher.containing(99).unwrap();
915 assert_eq!(bounds.epoch(), Epoch::new(0));
916
917 let bounds = epocher.containing(100).unwrap();
918 assert_eq!(bounds.epoch(), Epoch::new(1));
919 assert_eq!(bounds.first(), 100);
920 assert_eq!(bounds.last(), 199);
921
922 assert_eq!(epocher.first(Epoch::new(0)), Some(0));
924 assert_eq!(epocher.last(Epoch::new(0)), Some(99));
925 assert_eq!(epocher.first(Epoch::new(1)), Some(100));
926 assert_eq!(epocher.last(Epoch::new(1)), Some(199));
927 assert_eq!(epocher.first(Epoch::new(5)), Some(500));
928 assert_eq!(epocher.last(Epoch::new(5)), Some(599));
929 }
930
931 #[test]
932 fn test_epoch_bounds_relative() {
933 let epocher = FixedEpocher::new(NZU64!(100));
934
935 assert_eq!(epocher.containing(0).unwrap().relative(), 0);
937 assert_eq!(epocher.containing(50).unwrap().relative(), 50);
938 assert_eq!(epocher.containing(99).unwrap().relative(), 99);
939
940 assert_eq!(epocher.containing(100).unwrap().relative(), 0);
942 assert_eq!(epocher.containing(150).unwrap().relative(), 50);
943 assert_eq!(epocher.containing(199).unwrap().relative(), 99);
944
945 assert_eq!(epocher.containing(500).unwrap().relative(), 0);
947 assert_eq!(epocher.containing(567).unwrap().relative(), 67);
948 assert_eq!(epocher.containing(599).unwrap().relative(), 99);
949 }
950
951 #[test]
952 fn test_epoch_bounds_phase() {
953 let epocher = FixedEpocher::new(NZU64!(30));
955
956 assert_eq!(epocher.containing(0).unwrap().phase(), EpochPhase::Early);
958 assert_eq!(epocher.containing(14).unwrap().phase(), EpochPhase::Early);
959
960 assert_eq!(
962 epocher.containing(15).unwrap().phase(),
963 EpochPhase::Midpoint
964 );
965
966 assert_eq!(epocher.containing(16).unwrap().phase(), EpochPhase::Late);
968 assert_eq!(epocher.containing(29).unwrap().phase(), EpochPhase::Late);
969
970 assert_eq!(epocher.containing(30).unwrap().phase(), EpochPhase::Early);
972 assert_eq!(epocher.containing(44).unwrap().phase(), EpochPhase::Early);
973 assert_eq!(
974 epocher.containing(45).unwrap().phase(),
975 EpochPhase::Midpoint
976 );
977 assert_eq!(epocher.containing(46).unwrap().phase(), EpochPhase::Late);
978
979 let epocher = FixedEpocher::new(NZU64!(10));
981 assert_eq!(epocher.containing(0).unwrap().phase(), EpochPhase::Early);
982 assert_eq!(epocher.containing(4).unwrap().phase(), EpochPhase::Early);
983 assert_eq!(epocher.containing(5).unwrap().phase(), EpochPhase::Midpoint);
984 assert_eq!(epocher.containing(6).unwrap().phase(), EpochPhase::Late);
985 assert_eq!(epocher.containing(9).unwrap().phase(), EpochPhase::Late);
986
987 let epocher = FixedEpocher::new(NZU64!(11));
989 assert_eq!(epocher.containing(0).unwrap().phase(), EpochPhase::Early);
990 assert_eq!(epocher.containing(4).unwrap().phase(), EpochPhase::Early);
991 assert_eq!(epocher.containing(5).unwrap().phase(), EpochPhase::Midpoint);
992 assert_eq!(epocher.containing(6).unwrap().phase(), EpochPhase::Late);
993 assert_eq!(epocher.containing(10).unwrap().phase(), EpochPhase::Late);
994 }
995
996 #[test]
997 fn test_fixed_epocher_overflow() {
998 let epocher = FixedEpocher::new(NZU64!(100));
1000
1001 let last_valid_first = 18446744073709551500u64;
1010 let last_valid_last = 18446744073709551599u64;
1011
1012 let result = epocher.containing(last_valid_first);
1013 assert!(result.is_some());
1014 let bounds = result.unwrap();
1015 assert_eq!(bounds.first(), last_valid_first);
1016 assert_eq!(bounds.last(), last_valid_last);
1017
1018 let result = epocher.containing(last_valid_last);
1019 assert!(result.is_some());
1020 assert_eq!(result.unwrap().last(), last_valid_last);
1021
1022 let overflow_height = last_valid_last + 1;
1024 assert!(epocher.containing(overflow_height).is_none());
1025
1026 assert!(epocher.containing(u64::MAX).is_none());
1028
1029 let epocher = FixedEpocher::new(NZU64!(2));
1031
1032 let result = epocher.containing(u64::MAX - 1);
1034 assert!(result.is_some());
1035 assert_eq!(result.unwrap().last(), u64::MAX);
1036
1037 let result = epocher.containing(u64::MAX);
1040 assert!(result.is_some());
1041 assert_eq!(result.unwrap().last(), u64::MAX);
1042
1043 let epocher = FixedEpocher::new(NZU64!(1));
1045 let result = epocher.containing(u64::MAX);
1046 assert!(result.is_some());
1047 assert_eq!(result.unwrap().last(), u64::MAX);
1048
1049 let epocher = FixedEpocher::new(NZU64!(u64::MAX));
1051 assert!(epocher.containing(u64::MAX).is_none());
1052
1053 let epocher = FixedEpocher::new(NZU64!(100));
1055 let last_valid_epoch = Epoch::new(184467440737095515);
1056 let first_invalid_epoch = Epoch::new(184467440737095516);
1057
1058 assert!(epocher.first(last_valid_epoch).is_some());
1060 assert!(epocher.last(last_valid_epoch).is_some());
1061 let first = epocher.first(last_valid_epoch).unwrap();
1062 assert!(epocher.containing(first).is_some());
1063 assert_eq!(
1064 epocher.containing(first).unwrap().last(),
1065 epocher.last(last_valid_epoch).unwrap()
1066 );
1067
1068 assert!(epocher.first(first_invalid_epoch).is_none());
1070 assert!(epocher.last(first_invalid_epoch).is_none());
1071 assert!(epocher.containing(last_valid_last + 1).is_none());
1072 }
1073
1074 #[cfg(feature = "arbitrary")]
1075 mod conformance {
1076 use super::*;
1077 use commonware_codec::conformance::CodecConformance;
1078
1079 commonware_conformance::conformance_tests! {
1080 CodecConformance<Epoch>,
1081 CodecConformance<View>,
1082 CodecConformance<Round>,
1083 }
1084 }
1085}