1use crate::api::values_within_max;
4use crate::compositions::allocation_snapshot::AllocationSnapshot as AllocationSnapshotCarrier;
5use crate::compositions::bisection::Bisection as BisectionCarrier;
6use crate::compositions::equivalence_class::EquivalenceClass as EquivalenceClassCarrier;
7use crate::compositions::federated_budget::FederatedBudget as FederatedBudgetCarrier;
8use crate::compositions::rate_limit::RateLimit as RateLimitCarrier;
9use crate::compositions::reduction::Reducer as ReductionCarrier;
10use crate::compositions::relationship_graph::RelationshipGraph as RelationshipGraphCarrier;
11use crate::compositions::sampler::Sampler as SamplerCarrier;
12use crate::compositions::select_then_actuate::SelectThenActuate as SelectThenActuateCarrier;
13use crate::compositions::signal::Signal as SignalCarrier;
14use crate::compositions::traversal_engine::TraversalEngine as TraversalEngineCarrier;
15use vstd::prelude::*;
16
17verus! {
18
19#[derive(Clone, Copy, Debug, Eq, PartialEq)]
21#[non_exhaustive]
22pub enum AllocationSnapshotError {
23 NodeOutOfRange,
25 NodeAlreadyAccepted,
27 ZeroCost,
29 InsufficientBudget,
31}
32
33pub struct AllocationSnapshot {
46 inner: AllocationSnapshotCarrier,
47}
48
49impl AllocationSnapshot {
50 #[verifier::type_invariant]
51 closed spec fn well_formed(&self) -> bool {
52 self.inner.type_invariant() && self.inner.budget_consistency()
53 }
54
55 pub fn new(capacity: u64, num_nodes: u64) -> (snapshot: Self) {
57 Self { inner: AllocationSnapshotCarrier::new(capacity, num_nodes) }
58 }
59
60 pub fn capacity(&self) -> u64 { self.inner.budget.capacity }
62
63 pub fn num_nodes(&self) -> u64 { self.inner.num_nodes }
65
66 pub fn total_cost(&self) -> u64 { self.inner.budget.allocated }
68
69 pub fn budget_remaining(&self) -> u64 {
71 proof { use_type_invariant(&*self); }
72 self.inner.budget.available()
73 }
74
75 pub fn len(&self) -> usize { self.inner.registry.entries.len() }
77
78 pub fn is_empty(&self) -> bool { self.inner.registry.entries.is_empty() }
80
81 pub fn contains(&self, node: u64) -> bool {
83 proof { use_type_invariant(&*self); }
84 self.inner.contains_exec(node)
85 }
86
87 #[expect(clippy::indexing_slicing, reason = "the branch proves the accepted-node index is in bounds")]
89 pub fn accepted(&self, index: usize) -> Option<u64> {
90 if index < self.inner.registry.entries.len() {
91 Some(self.inner.registry.entries[index].0)
92 } else {
93 None
94 }
95 }
96
97 #[expect(clippy::indexing_slicing, reason = "the branch proves the registry index is in bounds")]
99 pub fn accepted_cost(&self, index: usize) -> Option<u64> {
100 if index < self.inner.registry.entries.len() {
101 Some(self.inner.registry.entries[index].1)
102 } else {
103 None
104 }
105 }
106
107 pub fn accept(&mut self, node: u64, cost: u64) -> (result: Result<(), AllocationSnapshotError>) {
114 proof { use_type_invariant(&*self); }
115 if node >= self.inner.num_nodes { return Err(AllocationSnapshotError::NodeOutOfRange); }
116 if self.inner.contains_exec(node) { return Err(AllocationSnapshotError::NodeAlreadyAccepted); }
117 if cost == 0 { return Err(AllocationSnapshotError::ZeroCost); }
118 let available = self.inner.budget.available();
119 if cost > available { return Err(AllocationSnapshotError::InsufficientBudget); }
120 let mut carrier = allocation_snapshot_sentinel();
121 core::mem::swap(&mut self.inner, &mut carrier);
122 carrier.accept_node(node, cost);
123 core::mem::swap(&mut self.inner, &mut carrier);
124 Ok(())
125 }
126}
127
128pub struct FederatedBudget {
141 inner: FederatedBudgetCarrier,
142}
143
144impl FederatedBudget {
145 #[verifier::type_invariant]
146 closed spec fn well_formed(&self) -> bool { self.inner.inv() }
147
148 pub fn new(master_capacity: u64, num_pools: usize) -> (budget: Self) {
150 Self { inner: FederatedBudgetCarrier::new(master_capacity, num_pools) }
151 }
152
153 pub fn master_capacity(&self) -> u64 { self.inner.master.capacity }
155
156 pub fn master_allocated(&self) -> u64 { self.inner.master.allocated }
158
159 pub fn len(&self) -> usize { self.inner.sub_pools.len() }
161
162 pub fn is_empty(&self) -> bool { self.inner.sub_pools.is_empty() }
164
165 #[expect(clippy::indexing_slicing, reason = "the branch proves the pool index is in bounds")]
167 pub fn pool_capacity(&self, pool: usize) -> Option<u64> {
168 proof { use_type_invariant(&*self); }
169 if pool < self.inner.sub_pools.len() {
170 Some(self.inner.sub_pools[pool].allocated + self.inner.sub_pools[pool].reserved)
171 } else {
172 None
173 }
174 }
175
176 #[expect(clippy::indexing_slicing, reason = "the branch proves the pool index is in bounds")]
178 pub fn pool_allocated(&self, pool: usize) -> Option<u64> {
179 if pool < self.inner.sub_pools.len() {
180 Some(self.inner.sub_pools[pool].allocated)
181 } else {
182 None
183 }
184 }
185
186 #[must_use]
188 pub fn try_delegate(&mut self, pool: usize, amount: u64) -> (accepted: bool) {
189 proof { use_type_invariant(&*self); }
190 let mut carrier = federated_budget_sentinel();
191 core::mem::swap(&mut self.inner, &mut carrier);
192 let accepted = carrier.allocate_sub_pool(pool, amount);
193 core::mem::swap(&mut self.inner, &mut carrier);
194 accepted
195 }
196
197 #[must_use]
199 pub fn try_allocate(&mut self, pool: usize, amount: u64) -> (accepted: bool) {
200 proof { use_type_invariant(&*self); }
201 let mut carrier = federated_budget_sentinel();
202 core::mem::swap(&mut self.inner, &mut carrier);
203 let accepted = carrier.allocate_from_sub_pool(pool, amount);
204 core::mem::swap(&mut self.inner, &mut carrier);
205 accepted
206 }
207
208 #[must_use]
210 pub fn try_release(&mut self, pool: usize, amount: u64) -> (accepted: bool) {
211 proof { use_type_invariant(&*self); }
212 let mut carrier = federated_budget_sentinel();
213 core::mem::swap(&mut self.inner, &mut carrier);
214 let accepted = carrier.release_from_sub_pool(pool, amount);
215 core::mem::swap(&mut self.inner, &mut carrier);
216 accepted
217 }
218}
219
220#[derive(Clone, Copy, Debug, Eq, PartialEq)]
222#[non_exhaustive]
223pub enum BisectionBuildError {
224 DomainTooSmall,
226 ThresholdOutOfRange,
228}
229
230#[derive(Clone, Copy, Debug, Eq, PartialEq)]
232#[non_exhaustive]
233pub enum BisectionError {
234 AlreadyConverged,
236}
237
238pub struct Bisection {
251 inner: BisectionCarrier,
252}
253
254impl Bisection {
255 #[verifier::type_invariant]
256 closed spec fn well_formed(&self) -> bool { self.inner.invariant() }
257
258 pub fn new(domain_size: u64, threshold: u64) -> (result: Result<Self, BisectionBuildError>) {
265 if domain_size < 2 { return Err(BisectionBuildError::DomainTooSmall); }
266 if threshold < 1 || threshold >= domain_size {
267 return Err(BisectionBuildError::ThresholdOutOfRange);
268 }
269 proof { lemma_u64_domain_fits_64(domain_size); }
270 let inner = BisectionCarrier::new(0, domain_size, threshold, domain_size, 64);
271 Ok(Self { inner })
272 }
273
274 pub fn lower(&self) -> u64 { self.inner.lo }
276
277 pub fn upper(&self) -> u64 { self.inner.hi }
279
280 pub fn threshold(&self) -> u64 { self.inner.threshold }
282
283 pub fn probes_taken(&self) -> u64 { self.inner.budget.allocated }
285
286 pub fn max_probes(&self) -> u64 { self.inner.budget.capacity }
288
289 pub fn is_converged(&self) -> bool {
291 proof { use_type_invariant(&*self); }
292 self.inner.converged()
293 }
294
295 pub fn probe(&mut self) -> (result: Result<(), BisectionError>) {
301 proof { use_type_invariant(&*self); }
302 if self.inner.hi - self.inner.lo < 2 { return Err(BisectionError::AlreadyConverged); }
303 let mut carrier = bisection_sentinel();
304 core::mem::swap(&mut self.inner, &mut carrier);
305 carrier.probe();
306 core::mem::swap(&mut self.inner, &mut carrier);
307 Ok(())
308 }
309
310 pub fn converge(&mut self) {
312 proof { use_type_invariant(&*self); }
313 let mut carrier = bisection_sentinel();
314 core::mem::swap(&mut self.inner, &mut carrier);
315 carrier.bisect();
316 core::mem::swap(&mut self.inner, &mut carrier);
317 }
318}
319
320#[derive(Clone, Copy, Debug, Eq, PartialEq)]
322#[non_exhaustive]
323pub enum EquivalenceClassError {
324 ElementOutOfRange,
326}
327
328pub struct EquivalenceClass {
341 inner: EquivalenceClassCarrier,
342}
343
344impl EquivalenceClass {
345 #[verifier::type_invariant]
346 closed spec fn well_formed(&self) -> bool { self.inner.inv() }
347
348 pub fn new(elements: usize, max_unions: u64) -> (classes: Self) {
350 Self { inner: EquivalenceClassCarrier::new(elements, max_unions) }
351 }
352
353 pub fn len(&self) -> usize { self.inner.n }
355
356 pub fn is_empty(&self) -> bool { self.inner.n == 0 }
358
359 pub fn unions_performed(&self) -> u64 { self.inner.budget.allocated }
361
362 pub fn max_unions(&self) -> u64 { self.inner.budget.capacity }
364
365 pub fn representative(&self, element: usize) -> (result: Result<usize, EquivalenceClassError>) {
371 proof { use_type_invariant(&*self); }
372 if element >= self.inner.n { return Err(EquivalenceClassError::ElementOutOfRange); }
373 Ok(self.inner.find(element))
374 }
375
376 pub fn union(&mut self, left: usize, right: usize) -> (result: Result<bool, EquivalenceClassError>) {
382 proof { use_type_invariant(&*self); }
383 if left >= self.inner.n || right >= self.inner.n {
384 return Err(EquivalenceClassError::ElementOutOfRange);
385 }
386 let mut carrier = equivalence_class_sentinel();
387 core::mem::swap(&mut self.inner, &mut carrier);
388 let merged = carrier.union(left, right);
389 core::mem::swap(&mut self.inner, &mut carrier);
390 Ok(merged)
391 }
392
393 pub fn equivalent(&self, left: usize, right: usize) -> (result: Result<bool, EquivalenceClassError>) {
399 proof { use_type_invariant(&*self); }
400 if left >= self.inner.n || right >= self.inner.n {
401 return Err(EquivalenceClassError::ElementOutOfRange);
402 }
403 Ok(self.inner.same(left, right))
404 }
405}
406
407#[derive(Clone, Copy, Debug, Eq, PartialEq)]
409#[non_exhaustive]
410pub enum RateLimitBuildError {
411 ZeroLimit,
413}
414
415#[derive(Clone, Copy, Debug, Eq, PartialEq)]
417#[non_exhaustive]
418pub enum RateLimitError {
419 ClockExhausted,
421}
422
423pub struct RateLimit {
436 inner: RateLimitCarrier,
437}
438
439impl RateLimit {
440 #[verifier::type_invariant]
441 closed spec fn well_formed(&self) -> bool {
442 self.inner.type_invariant() && self.inner.window_start_not_future()
443 }
444
445 pub fn new(max_per_window: u64, window_duration: u64, max_clock: u64)
451 -> (result: Result<Self, RateLimitBuildError>) {
452 if max_per_window == 0 { return Err(RateLimitBuildError::ZeroLimit); }
453 Ok(Self { inner: RateLimitCarrier::new(max_per_window, window_duration, max_clock) })
454 }
455
456 pub fn max_per_window(&self) -> u64 { self.inner.budget.capacity }
458
459 pub fn window_duration(&self) -> u64 { self.inner.window_duration }
461
462 pub fn count(&self) -> u64 { self.inner.budget.allocated }
464
465 pub fn clock(&self) -> u64 { self.inner.clock }
467
468 pub fn window_start(&self) -> u64 { self.inner.window_start }
470
471 #[must_use]
473 pub fn try_acquire(&mut self) -> (accepted: bool) {
474 proof { use_type_invariant(&*self); }
475 let mut carrier = rate_limit_sentinel();
476 core::mem::swap(&mut self.inner, &mut carrier);
477 let accepted = carrier.try_acquire();
478 core::mem::swap(&mut self.inner, &mut carrier);
479 accepted
480 }
481
482 pub fn tick(&mut self) -> (result: Result<(), RateLimitError>) {
488 proof { use_type_invariant(&*self); }
489 if self.inner.clock >= self.inner.max_clock { return Err(RateLimitError::ClockExhausted); }
490 let mut carrier = rate_limit_sentinel();
491 core::mem::swap(&mut self.inner, &mut carrier);
492 carrier.tick();
493 core::mem::swap(&mut self.inner, &mut carrier);
494 Ok(())
495 }
496}
497
498#[derive(Clone, Copy, Debug, Eq, PartialEq)]
500#[non_exhaustive]
501pub enum ReductionBuildError {
502 TooManyItems,
504 ValueOutOfRange,
506}
507
508#[derive(Clone, Copy, Debug, Eq, PartialEq)]
510#[non_exhaustive]
511pub enum ReductionError {
512 Complete,
514}
515
516pub struct Reduction {
529 inner: ReductionCarrier,
530}
531
532impl Reduction {
533 #[verifier::type_invariant]
534 closed spec fn well_formed(&self) -> bool {
535 self.inner.inv()
536 }
537
538 pub fn new(items: Vec<u64>) -> (result: Result<Self, ReductionBuildError>) {
544 if items.len() > 1_000_000_000 { return Err(ReductionBuildError::TooManyItems); }
545 if !values_within_max(&items, 1_000_000_000) {
546 return Err(ReductionBuildError::ValueOutOfRange);
547 }
548 Ok(Self { inner: ReductionCarrier::new(items) })
549 }
550
551 pub fn result(&self) -> u64 { self.inner.result() }
553
554 pub fn processed_len(&self) -> usize { self.inner.position() }
556
557 pub fn remaining_len(&self) -> usize {
559 proof { use_type_invariant(&*self); }
560 self.inner.remaining_len()
561 }
562
563 pub fn is_complete(&self) -> bool {
565 proof { use_type_invariant(&*self); }
566 self.inner.done()
567 }
568
569 pub fn process_next(&mut self) -> (result: Result<(), ReductionError>) {
575 proof { use_type_invariant(&*self); }
576 if self.inner.done() { return Err(ReductionError::Complete); }
577 let mut carrier = reduction_sentinel();
578 core::mem::swap(&mut self.inner, &mut carrier);
579 carrier.process();
580 core::mem::swap(&mut self.inner, &mut carrier);
581 Ok(())
582 }
583}
584
585#[derive(Clone, Copy, Debug, Eq, PartialEq)]
587#[non_exhaustive]
588pub enum RelationshipGraphError {
589 NodeOutOfRange,
591 WeightOutOfRange,
593 SelfLoop,
595}
596
597pub struct RelationshipGraph {
610 inner: RelationshipGraphCarrier,
611}
612
613impl RelationshipGraph {
614 #[verifier::type_invariant]
615 closed spec fn well_formed(&self) -> bool { self.inner.inv() }
616
617 pub fn new(num_nodes: usize, max_weight: u64) -> (graph: Self) {
619 Self { inner: RelationshipGraphCarrier::new(num_nodes, max_weight) }
620 }
621
622 pub fn num_nodes(&self) -> usize { self.inner.num_nodes }
624
625 pub fn max_weight(&self) -> u64 { self.inner.max_weight }
627
628 pub fn edge_count(&self) -> usize { self.inner.registry.entries.len() }
630
631 pub fn edge(&self, index: usize) -> Option<(usize, usize, u64)> {
633 if index < self.inner.registry.entries.len() {
634 Some(self.inner.registry.entries[index].0)
635 } else {
636 None
637 }
638 }
639
640 pub fn contains(&self, source: usize, destination: usize) -> bool {
642 proof { use_type_invariant(&*self); }
643 self.inner.contains_pair(source, destination)
644 }
645
646 pub fn add_edge(&mut self, source: usize, destination: usize, weight: u64)
652 -> (result: Result<bool, RelationshipGraphError>) {
653 proof { use_type_invariant(&*self); }
654 if source >= self.inner.num_nodes || destination >= self.inner.num_nodes {
655 return Err(RelationshipGraphError::NodeOutOfRange);
656 }
657 if weight > self.inner.max_weight { return Err(RelationshipGraphError::WeightOutOfRange); }
658 if source == destination { return Err(RelationshipGraphError::SelfLoop); }
659 let mut carrier = relationship_graph_sentinel();
660 core::mem::swap(&mut self.inner, &mut carrier);
661 let added = carrier.add_edge(source, destination, weight);
662 core::mem::swap(&mut self.inner, &mut carrier);
663 Ok(added)
664 }
665
666 pub fn remove_edges(&mut self, source: usize, destination: usize) {
668 proof { use_type_invariant(&*self); }
669 let mut carrier = relationship_graph_sentinel();
670 core::mem::swap(&mut self.inner, &mut carrier);
671 carrier.remove_edge(source, destination);
672 core::mem::swap(&mut self.inner, &mut carrier);
673 }
674}
675
676#[derive(Clone, Copy, Debug, Eq, PartialEq)]
678#[non_exhaustive]
679pub enum SamplerError {
680 ItemOutOfRange,
682 SampleFull,
684 OutsideSupport,
686 AlreadySelected,
688}
689
690pub struct Sampler {
703 inner: SamplerCarrier,
704}
705
706impl Sampler {
707 #[verifier::type_invariant]
708 closed spec fn well_formed(&self) -> bool { self.inner.inv() }
709
710 pub fn new(distribution: Vec<u64>, sample_size: usize) -> (sampler: Self) {
712 Self { inner: SamplerCarrier::new(distribution, sample_size) }
713 }
714
715 pub fn len(&self) -> usize { self.inner.actuation.num_seats }
717
718 pub fn is_empty(&self) -> bool { self.inner.actuation.num_seats == 0 }
720
721 pub fn sample_size(&self) -> usize { self.inner.budget.capacity as usize }
723
724 pub fn selected_len(&self) -> usize { self.inner.budget.allocated as usize }
726
727 pub fn weight(&self, item: usize) -> Option<u64> {
729 proof { use_type_invariant(&*self); }
730 if item < self.inner.actuation.num_seats { Some(self.inner.weight(item)) } else { None }
731 }
732
733 pub fn contains(&self, item: usize) -> bool {
735 proof { use_type_invariant(&*self); }
736 self.inner.contains_exec(item)
737 }
738
739 pub fn sample(&mut self, item: usize) -> (result: Result<(), SamplerError>) {
746 proof { use_type_invariant(&*self); }
747 if item >= self.inner.actuation.num_seats { return Err(SamplerError::ItemOutOfRange); }
748 if self.inner.budget.allocated >= self.inner.budget.capacity {
749 return Err(SamplerError::SampleFull);
750 }
751 if self.inner.weight(item) == 0 { return Err(SamplerError::OutsideSupport); }
752 if self.inner.contains_exec(item) { return Err(SamplerError::AlreadySelected); }
753 let mut carrier = sampler_sentinel();
754 core::mem::swap(&mut self.inner, &mut carrier);
755 carrier.sample(item);
756 core::mem::swap(&mut self.inner, &mut carrier);
757 Ok(())
758 }
759
760 #[must_use]
762 pub fn zero(&mut self, item: usize) -> (accepted: bool) {
763 proof { use_type_invariant(&*self); }
764 let mut carrier = sampler_sentinel();
765 core::mem::swap(&mut self.inner, &mut carrier);
766 let accepted = carrier.zero(item);
767 core::mem::swap(&mut self.inner, &mut carrier);
768 accepted
769 }
770
771 pub fn draw_weighted(&mut self, item: usize, entropy: u64) -> (result: Result<bool, SamplerError>) {
777 proof { use_type_invariant(&*self); }
778 if item >= self.inner.actuation.num_seats { return Err(SamplerError::ItemOutOfRange); }
779 let mut carrier = sampler_sentinel();
780 core::mem::swap(&mut self.inner, &mut carrier);
781 let accepted = carrier.draw_weighted(item, entropy);
782 core::mem::swap(&mut self.inner, &mut carrier);
783 Ok(accepted)
784 }
785
786 pub fn draw_uniform(&mut self, item: usize) -> (result: Result<bool, SamplerError>) {
792 proof { use_type_invariant(&*self); }
793 if item >= self.inner.actuation.num_seats { return Err(SamplerError::ItemOutOfRange); }
794 let mut carrier = sampler_sentinel();
795 core::mem::swap(&mut self.inner, &mut carrier);
796 let accepted = carrier.draw_uniform(item);
797 core::mem::swap(&mut self.inner, &mut carrier);
798 Ok(accepted)
799 }
800}
801
802#[derive(Clone, Copy, Debug, Eq, PartialEq)]
804#[non_exhaustive]
805pub enum SignalBuildError {
806 InitialValueOutOfRange,
808}
809
810#[derive(Clone, Copy, Debug, Eq, PartialEq)]
812#[non_exhaustive]
813pub enum SignalError {
814 ValueOutOfRange,
816 ListenerOutOfRange,
818 ListenerNotPending,
820 ChangeCapacityExhausted,
822}
823
824pub struct Signal {
838 inner: SignalCarrier,
839}
840
841impl Signal {
842 #[verifier::type_invariant]
843 closed spec fn well_formed(&self) -> bool {
844 self.inner.inv()
845 }
846
847 pub fn new(initial_value: u64, num_values: u64, num_listeners: usize)
854 -> (result: Result<Self, SignalBuildError>) {
855 Self::with_change_capacity(initial_value, num_values, num_listeners, usize::MAX)
856 }
857
858 pub fn with_change_capacity(
865 initial_value: u64,
866 num_values: u64,
867 num_listeners: usize,
868 max_changes: usize,
869 ) -> (result: Result<Self, SignalBuildError>) {
870 if initial_value >= num_values { return Err(SignalBuildError::InitialValueOutOfRange); }
871 Ok(Self { inner: SignalCarrier::new(initial_value, num_values, num_listeners, max_changes) })
872 }
873
874 pub fn value(&self) -> u64 {
876 proof { use_type_invariant(&*self); }
877 self.inner.current_value()
878 }
879
880 pub fn listener_count(&self) -> usize { self.inner.num_listeners }
882
883 pub fn change_observed(&self) -> bool { !self.inner.audit.log.is_empty() }
885
886 pub fn change_capacity(&self) -> usize { self.inner.audit.max_log_len }
888
889 pub fn change_count(&self) -> usize { self.inner.audit.log.len() }
891
892 pub fn is_pending(&self, listener: usize) -> Option<bool> {
894 proof { use_type_invariant(&*self); }
895 if listener < self.inner.num_listeners { Some(self.inner.is_pending(listener)) } else { None }
896 }
897
898 pub fn is_notified(&self, listener: usize) -> Option<bool> {
900 proof { use_type_invariant(&*self); }
901 if listener < self.inner.num_listeners { Some(self.inner.is_notified(listener)) } else { None }
902 }
903
904 pub fn set_value(&mut self, value: u64) -> (result: Result<bool, SignalError>) {
911 proof { use_type_invariant(&*self); }
912 if value >= self.inner.num_values { return Err(SignalError::ValueOutOfRange); }
913 let current = self.inner.current_value();
914 if value == current { return Ok(false); }
915 if self.inner.audit.log.len() >= self.inner.audit.max_log_len {
916 return Err(SignalError::ChangeCapacityExhausted);
917 }
918 let mut carrier = signal_sentinel();
919 core::mem::swap(&mut self.inner, &mut carrier);
920 carrier.set_value(value);
921 core::mem::swap(&mut self.inner, &mut carrier);
922 Ok(true)
923 }
924
925 pub fn notify(&mut self, listener: usize) -> (result: Result<(), SignalError>) {
931 proof { use_type_invariant(&*self); }
932 if listener >= self.inner.num_listeners { return Err(SignalError::ListenerOutOfRange); }
933 if !self.inner.is_pending(listener) { return Err(SignalError::ListenerNotPending); }
934 let mut carrier = signal_sentinel();
935 core::mem::swap(&mut self.inner, &mut carrier);
936 carrier.notify_listener(listener);
937 core::mem::swap(&mut self.inner, &mut carrier);
938 Ok(())
939 }
940}
941
942#[derive(Clone, Copy, Debug, Eq, PartialEq)]
944#[non_exhaustive]
945pub enum TraversalBuildError {
946 NoNodes,
948 RootOutOfRange,
950}
951
952#[derive(Clone, Copy, Debug, Eq, PartialEq)]
954#[non_exhaustive]
955pub enum TraversalError {
956 NodeOutOfRange,
958 NodeNotQueued,
960 NodeAlreadyVisited,
962 QueueNotEmpty,
964}
965
966pub struct TraversalEngine {
979 inner: TraversalEngineCarrier,
980}
981
982impl TraversalEngine {
983 #[verifier::type_invariant]
984 closed spec fn well_formed(&self) -> bool {
985 self.inner.inv()
986 }
987
988 pub fn new(num_nodes: usize, root: usize, budget: u64)
994 -> (result: Result<Self, TraversalBuildError>) {
995 if num_nodes == 0 { return Err(TraversalBuildError::NoNodes); }
996 if root >= num_nodes { return Err(TraversalBuildError::RootOutOfRange); }
997 Ok(Self { inner: TraversalEngineCarrier::new(num_nodes, root, budget) })
998 }
999
1000 pub fn num_nodes(&self) -> usize { self.inner.num_nodes }
1002
1003 pub fn root(&self) -> usize { self.inner.root }
1005
1006 pub fn budget_remaining(&self) -> u64 {
1008 proof { use_type_invariant(&*self); }
1009 self.inner.budget_remaining()
1010 }
1011
1012 pub fn queued_len(&self) -> usize { self.inner.queue.len() }
1014
1015 pub fn visited_len(&self) -> usize { self.inner.visited_count() }
1017
1018 pub fn accepted_len(&self) -> usize { self.inner.accepted.len() }
1020
1021 pub fn is_queued(&self, node: usize) -> bool { self.inner.queue_contains(node) }
1023
1024 pub fn is_visited(&self, node: usize) -> bool { self.inner.visited_contains(node) }
1026
1027 pub fn is_accepted(&self, node: usize) -> bool { self.inner.accepted_contains(node) }
1029
1030 pub fn visit(&mut self, node: usize) -> (result: Result<(), TraversalError>) {
1036 proof { use_type_invariant(&*self); }
1037 if node >= self.inner.num_nodes { return Err(TraversalError::NodeOutOfRange); }
1038 if !self.inner.queue_contains(node) { return Err(TraversalError::NodeNotQueued); }
1039 if self.inner.visited_contains(node) { return Err(TraversalError::NodeAlreadyVisited); }
1040 let mut carrier = traversal_engine_sentinel();
1041 core::mem::swap(&mut self.inner, &mut carrier);
1042 carrier.visit_node(node);
1043 core::mem::swap(&mut self.inner, &mut carrier);
1044 Ok(())
1045 }
1046
1047 pub fn skip(&mut self, node: usize) -> (result: Result<(), TraversalError>) {
1053 proof { use_type_invariant(&*self); }
1054 if node >= self.inner.num_nodes { return Err(TraversalError::NodeOutOfRange); }
1055 if !self.inner.queue_contains(node) { return Err(TraversalError::NodeNotQueued); }
1056 let mut carrier = traversal_engine_sentinel();
1057 core::mem::swap(&mut self.inner, &mut carrier);
1058 carrier.skip(node);
1059 core::mem::swap(&mut self.inner, &mut carrier);
1060 Ok(())
1061 }
1062
1063 pub fn terminate(&mut self) -> (result: Result<(), TraversalError>) {
1069 proof { use_type_invariant(&*self); }
1070 if !self.inner.queue.is_empty() { return Err(TraversalError::QueueNotEmpty); }
1071 let mut carrier = traversal_engine_sentinel();
1072 core::mem::swap(&mut self.inner, &mut carrier);
1073 carrier.terminate();
1074 core::mem::swap(&mut self.inner, &mut carrier);
1075 Ok(())
1076 }
1077}
1078
1079#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1081#[non_exhaustive]
1082pub enum SelectThenActuateBuildError {
1083 NoCandidates,
1085}
1086
1087#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1089#[non_exhaustive]
1090pub enum SelectThenActuateError {
1091 SeatOutOfRange,
1093 CandidateOutOfRange,
1095 PassComplete,
1097 SeatAlreadyAllocated,
1099 EffectAlreadyApplied,
1101 SeatNotAllocated,
1103 SeatAlreadyActuated,
1105 PassNotReady,
1107}
1108
1109pub struct SelectThenActuate {
1125 inner: SelectThenActuateCarrier,
1126}
1127
1128impl SelectThenActuate {
1129 #[verifier::type_invariant]
1130 closed spec fn well_formed(&self) -> bool { self.inner.inv() }
1131
1132 pub fn new(num_seats: usize, num_candidates: usize)
1138 -> (result: Result<Self, SelectThenActuateBuildError>) {
1139 if num_candidates == 0 { return Err(SelectThenActuateBuildError::NoCandidates); }
1140 Ok(Self { inner: SelectThenActuateCarrier::new(num_seats, num_candidates) })
1141 }
1142
1143 pub fn seat_count(&self) -> usize { self.inner.selections.len() }
1145
1146 pub fn candidate_count(&self) -> usize { self.inner.num_candidates }
1148
1149 pub fn score(&self, seat: usize, candidate: usize) -> Option<u64> {
1151 proof { use_type_invariant(&*self); }
1152 if seat >= self.inner.selections.len() || candidate >= self.inner.num_candidates {
1153 None
1154 } else {
1155 Some(self.inner.score_at(seat, candidate))
1156 }
1157 }
1158
1159 pub fn allocation(&self, seat: usize) -> Option<usize> {
1161 proof { use_type_invariant(&*self); }
1162 if seat >= self.inner.selections.len() { None } else { self.inner.allocation_at(seat) }
1163 }
1164
1165 pub fn is_actuated(&self, seat: usize) -> Option<bool> {
1167 proof { use_type_invariant(&*self); }
1168 if seat >= self.inner.selections.len() { None } else { Some(self.inner.is_actuated(seat)) }
1169 }
1170
1171 pub fn is_complete(&self) -> bool { self.inner.is_complete() }
1173
1174 pub fn update_score(
1180 &mut self,
1181 seat: usize,
1182 candidate: usize,
1183 value: u64,
1184 ) -> (result: Result<(), SelectThenActuateError>) {
1185 proof { use_type_invariant(&*self); }
1186 if seat >= self.inner.selections.len() {
1187 return Err(SelectThenActuateError::SeatOutOfRange);
1188 }
1189 if candidate >= self.inner.num_candidates {
1190 return Err(SelectThenActuateError::CandidateOutOfRange);
1191 }
1192 if self.inner.actuation.complete { return Err(SelectThenActuateError::PassComplete); }
1193 if self.inner.is_actuated(seat) {
1194 return Err(SelectThenActuateError::EffectAlreadyApplied);
1195 }
1196 let mut carrier = select_then_actuate_sentinel();
1197 core::mem::swap(&mut self.inner, &mut carrier);
1198 carrier.update_score(seat, candidate, value);
1199 core::mem::swap(&mut self.inner, &mut carrier);
1200 Ok(())
1201 }
1202
1203 pub fn evaluate(&mut self, seat: usize) -> (result: Result<usize, SelectThenActuateError>) {
1209 proof { use_type_invariant(&*self); }
1210 if seat >= self.inner.selections.len() {
1211 return Err(SelectThenActuateError::SeatOutOfRange);
1212 }
1213 if self.inner.actuation.complete { return Err(SelectThenActuateError::PassComplete); }
1214 if self.inner.is_allocated(seat) {
1215 return Err(SelectThenActuateError::SeatAlreadyAllocated);
1216 }
1217 let mut carrier = select_then_actuate_sentinel();
1218 core::mem::swap(&mut self.inner, &mut carrier);
1219 carrier.evaluate(seat);
1220 let winner = match carrier.allocation_at(seat) {
1221 Some(candidate) => candidate,
1222 None => {
1223 core::mem::swap(&mut self.inner, &mut carrier);
1224 return Err(SelectThenActuateError::SeatNotAllocated);
1225 },
1226 };
1227 core::mem::swap(&mut self.inner, &mut carrier);
1228 Ok(winner)
1229 }
1230
1231 pub fn actuate(&mut self, seat: usize) -> (result: Result<(), SelectThenActuateError>) {
1238 proof { use_type_invariant(&*self); }
1239 if seat >= self.inner.selections.len() {
1240 return Err(SelectThenActuateError::SeatOutOfRange);
1241 }
1242 if self.inner.actuation.complete { return Err(SelectThenActuateError::PassComplete); }
1243 if !self.inner.is_allocated(seat) {
1244 return Err(SelectThenActuateError::SeatNotAllocated);
1245 }
1246 if self.inner.is_actuated(seat) {
1247 return Err(SelectThenActuateError::SeatAlreadyActuated);
1248 }
1249 let mut carrier = select_then_actuate_sentinel();
1250 core::mem::swap(&mut self.inner, &mut carrier);
1251 carrier.actuate(seat);
1252 core::mem::swap(&mut self.inner, &mut carrier);
1253 Ok(())
1254 }
1255
1256 pub fn finish(&mut self) -> (result: Result<(), SelectThenActuateError>) {
1262 proof { use_type_invariant(&*self); }
1263 if self.inner.actuation.complete { return Err(SelectThenActuateError::PassComplete); }
1264 if !self.inner.can_finish() { return Err(SelectThenActuateError::PassNotReady); }
1265 let mut carrier = select_then_actuate_sentinel();
1266 core::mem::swap(&mut self.inner, &mut carrier);
1267 carrier.finish();
1268 core::mem::swap(&mut self.inner, &mut carrier);
1269 Ok(())
1270 }
1271}
1272
1273proof fn lemma_u64_domain_fits_64(value: u64)
1274 ensures value as int <= crate::compositions::bisection::pow2(64),
1275{
1276 assert(crate::compositions::bisection::pow2(64) == 18_446_744_073_709_551_616int) by (compute);
1277}
1278
1279fn allocation_snapshot_sentinel() -> (carrier: AllocationSnapshotCarrier)
1280 ensures carrier.type_invariant(), carrier.budget_consistency(),
1281{ AllocationSnapshotCarrier::new(0, 0) }
1282
1283fn federated_budget_sentinel() -> (carrier: FederatedBudgetCarrier)
1284 ensures carrier.inv(),
1285{ FederatedBudgetCarrier::new(0, 0) }
1286
1287fn bisection_sentinel() -> (carrier: BisectionCarrier)
1288 ensures carrier.invariant(),
1289{
1290 proof {
1291 assert(crate::compositions::bisection::pow2(1) == 2) by (compute);
1292 }
1293 BisectionCarrier::new(0, 2, 1, 2, 1)
1294}
1295
1296fn equivalence_class_sentinel() -> (carrier: EquivalenceClassCarrier)
1297 ensures carrier.inv(),
1298{ EquivalenceClassCarrier::new(0, 0) }
1299
1300fn rate_limit_sentinel() -> (carrier: RateLimitCarrier)
1301 ensures carrier.type_invariant(), carrier.window_start_not_future(),
1302{ RateLimitCarrier::new(1, 1, 0) }
1303
1304fn reduction_sentinel() -> (carrier: ReductionCarrier)
1305 ensures carrier.inv(),
1306{
1307 let values: Vec<u64> = Vec::new();
1308 ReductionCarrier::new(values)
1309}
1310
1311fn relationship_graph_sentinel() -> (carrier: RelationshipGraphCarrier)
1312 ensures carrier.inv(),
1313{ RelationshipGraphCarrier::new(0, 0) }
1314
1315fn sampler_sentinel() -> (carrier: SamplerCarrier)
1316 ensures carrier.inv(),
1317{
1318 let distribution: Vec<u64> = Vec::new();
1319 SamplerCarrier::new(distribution, 0)
1320}
1321
1322fn signal_sentinel() -> (carrier: SignalCarrier)
1323 ensures carrier.inv(),
1324{ SignalCarrier::new(0, 1, 0, 0) }
1325
1326fn traversal_engine_sentinel() -> (carrier: TraversalEngineCarrier)
1327 ensures carrier.inv(),
1328{ TraversalEngineCarrier::new(1, 0, 0) }
1329
1330fn select_then_actuate_sentinel() -> (carrier: SelectThenActuateCarrier)
1331 ensures carrier.inv(),
1332{ SelectThenActuateCarrier::new(0, 1) }
1333
1334}
1335
1336impl AllocationSnapshot {
1337 pub fn accepted_entries(&self) -> impl ExactSizeIterator<Item = &(u64, u64)> {
1339 self.inner.registry.entries.iter()
1340 }
1341}
1342
1343impl FederatedBudget {
1344 pub fn master_available(&self) -> u64 {
1346 self.inner.master.available()
1347 }
1348
1349 pub fn pools(&self) -> impl ExactSizeIterator<Item = (u64, u64)> + '_ {
1351 self.inner
1352 .sub_pools
1353 .iter()
1354 .map(|pool| (pool.allocated + pool.reserved, pool.allocated))
1355 }
1356}
1357
1358impl EquivalenceClass {
1359 pub fn representatives(&self) -> impl ExactSizeIterator<Item = (usize, usize)> + '_ {
1361 (0..self.len()).map(|element| (element, self.inner.find(element)))
1362 }
1363}
1364
1365impl RateLimit {
1366 pub fn max_clock(&self) -> u64 {
1368 self.inner.max_clock
1369 }
1370
1371 pub fn available(&self) -> u64 {
1373 self.inner.budget.available()
1374 }
1375}
1376
1377impl Reduction {
1378 pub fn items(&self) -> &[u64] {
1380 self.inner.source.as_slice()
1381 }
1382
1383 pub fn remaining(&self) -> &[u64] {
1385 &self.inner.source[self.processed_len()..]
1386 }
1387}
1388
1389impl RelationshipGraph {
1390 pub fn edges(&self) -> impl ExactSizeIterator<Item = (usize, usize, u64)> + '_ {
1392 self.inner.registry.entries.iter().map(|entry| entry.0)
1393 }
1394}
1395
1396impl Sampler {
1397 pub fn weights(&self) -> impl ExactSizeIterator<Item = u64> + '_ {
1399 self.inner
1400 .actuation
1401 .allocation
1402 .iter()
1403 .map(|entry| entry.unwrap_or(0))
1404 }
1405
1406 pub fn selected(&self) -> impl Iterator<Item = usize> + '_ {
1408 self.inner
1409 .actuation
1410 .effects
1411 .iter()
1412 .enumerate()
1413 .filter_map(|(item, effect)| effect.is_some().then_some(item))
1414 }
1415}
1416
1417impl Signal {
1418 pub fn value_domain_size(&self) -> u64 {
1420 self.inner.num_values
1421 }
1422
1423 pub fn listeners(&self) -> impl ExactSizeIterator<Item = (bool, bool)> + '_ {
1425 (0..self.listener_count()).map(|listener| {
1426 (
1427 self.inner.is_pending(listener),
1428 self.inner.is_notified(listener),
1429 )
1430 })
1431 }
1432}
1433
1434impl TraversalEngine {
1435 pub fn queued(&self) -> impl ExactSizeIterator<Item = &usize> {
1437 self.inner.queue.values.iter()
1438 }
1439
1440 pub fn visited(&self) -> impl Iterator<Item = usize> + '_ {
1442 self.inner
1443 .visited
1444 .iter()
1445 .enumerate()
1446 .filter_map(|(node, marker)| marker.marked.then_some(node))
1447 }
1448
1449 pub fn accepted(&self) -> impl ExactSizeIterator<Item = &usize> {
1451 self.inner.accepted.accumulated.iter()
1452 }
1453}
1454
1455impl SelectThenActuate {
1456 pub fn scores(&self, seat: usize) -> Option<&[u64]> {
1458 self.inner
1459 .selections
1460 .get(seat)
1461 .map(|selection| selection.scores.as_slice())
1462 }
1463
1464 pub fn allocations(&self) -> impl ExactSizeIterator<Item = Option<usize>> + '_ {
1466 self.inner
1467 .selections
1468 .iter()
1469 .map(|selection| selection.allocation)
1470 }
1471
1472 pub fn effects(&self) -> &[Option<u64>] {
1474 self.inner.actuation.effects.as_slice()
1475 }
1476}
1477
1478impl_observational_debug!(AllocationSnapshot, "AllocationSnapshot",
1479 "capacity" => capacity,
1480 "num_nodes" => num_nodes,
1481 "total_cost" => total_cost,
1482 "budget_remaining" => budget_remaining,
1483 "len" => len,
1484);
1485impl_observational_debug!(FederatedBudget, "FederatedBudget",
1486 "master_capacity" => master_capacity,
1487 "master_allocated" => master_allocated,
1488 "len" => len,
1489);
1490impl_observational_debug!(Bisection, "Bisection",
1491 "lower" => lower,
1492 "upper" => upper,
1493 "threshold" => threshold,
1494 "probes_taken" => probes_taken,
1495 "max_probes" => max_probes,
1496 "converged" => is_converged,
1497);
1498impl_observational_debug!(EquivalenceClass, "EquivalenceClass",
1499 "len" => len,
1500 "unions_performed" => unions_performed,
1501 "max_unions" => max_unions,
1502);
1503impl_observational_debug!(RateLimit, "RateLimit",
1504 "max_per_window" => max_per_window,
1505 "window_duration" => window_duration,
1506 "count" => count,
1507 "clock" => clock,
1508 "window_start" => window_start,
1509);
1510impl_observational_debug!(Reduction, "Reduction",
1511 "result" => result,
1512 "processed_len" => processed_len,
1513 "remaining_len" => remaining_len,
1514 "complete" => is_complete,
1515);
1516impl_observational_debug!(RelationshipGraph, "RelationshipGraph",
1517 "num_nodes" => num_nodes,
1518 "max_weight" => max_weight,
1519 "edge_count" => edge_count,
1520);
1521impl_observational_debug!(Sampler, "Sampler",
1522 "len" => len,
1523 "sample_size" => sample_size,
1524 "selected_len" => selected_len,
1525);
1526impl_observational_debug!(Signal, "Signal",
1527 "value" => value,
1528 "listener_count" => listener_count,
1529 "change_observed" => change_observed,
1530);
1531impl_observational_debug!(TraversalEngine, "TraversalEngine",
1532 "num_nodes" => num_nodes,
1533 "root" => root,
1534 "budget_remaining" => budget_remaining,
1535 "queued_len" => queued_len,
1536 "visited_len" => visited_len,
1537 "accepted_len" => accepted_len,
1538);
1539impl_observational_debug!(SelectThenActuate, "SelectThenActuate",
1540 "seat_count" => seat_count,
1541 "candidate_count" => candidate_count,
1542 "complete" => is_complete,
1543);
1544
1545impl_public_error!(AllocationSnapshotError, {
1546 Self::NodeOutOfRange => "node is outside the snapshot universe",
1547 Self::NodeAlreadyAccepted => "node is already accepted",
1548 Self::ZeroCost => "accepted node cost must be positive",
1549 Self::InsufficientBudget => "node cost exceeds the remaining budget",
1550});
1551impl_public_error!(BisectionBuildError, {
1552 Self::DomainTooSmall => "bisection domain must contain at least two points",
1553 Self::ThresholdOutOfRange => "bisection threshold is outside the domain",
1554});
1555impl_public_error!(BisectionError, { Self::AlreadyConverged => "bisection is already converged" });
1556impl_public_error!(EquivalenceClassError, { Self::ElementOutOfRange => "element is outside the partition" });
1557impl_public_error!(RateLimitBuildError, { Self::ZeroLimit => "rate limit must admit at least one operation" });
1558impl_public_error!(RateLimitError, { Self::ClockExhausted => "rate-limit logical clock is exhausted" });
1559impl_public_error!(ReductionBuildError, {
1560 Self::TooManyItems => "reduction input exceeds the verified item ceiling",
1561 Self::ValueOutOfRange => "reduction input exceeds the verified value ceiling",
1562});
1563impl_public_error!(ReductionError, { Self::Complete => "reduction is already complete" });
1564impl_public_error!(RelationshipGraphError, {
1565 Self::NodeOutOfRange => "graph node is outside the configured universe",
1566 Self::WeightOutOfRange => "edge weight exceeds the configured maximum",
1567 Self::SelfLoop => "relationship graph does not admit self-loops",
1568});
1569impl_public_error!(SamplerError, {
1570 Self::ItemOutOfRange => "sample item is outside the distribution",
1571 Self::SampleFull => "bounded sample is full",
1572 Self::OutsideSupport => "sample item has zero support weight",
1573 Self::AlreadySelected => "sample item is already selected",
1574});
1575impl_public_error!(SignalBuildError, { Self::InitialValueOutOfRange => "initial signal value is outside its universe" });
1576impl_public_error!(SignalError, {
1577 Self::ValueOutOfRange => "signal value is outside its universe",
1578 Self::ListenerOutOfRange => "listener is outside the signal universe",
1579 Self::ListenerNotPending => "listener has no pending notification",
1580 Self::ChangeCapacityExhausted => "signal change capacity is exhausted",
1581});
1582impl_public_error!(TraversalBuildError, {
1583 Self::NoNodes => "traversal requires at least one node",
1584 Self::RootOutOfRange => "traversal root is outside the node universe",
1585});
1586impl_public_error!(TraversalError, {
1587 Self::NodeOutOfRange => "traversal node is outside the configured universe",
1588 Self::NodeNotQueued => "traversal node is not queued",
1589 Self::NodeAlreadyVisited => "traversal node was already visited",
1590 Self::QueueNotEmpty => "traversal queue is not empty",
1591});
1592impl_public_error!(SelectThenActuateBuildError, {
1593 Self::NoCandidates => "select-then-actuate requires at least one candidate",
1594});
1595impl_public_error!(SelectThenActuateError, {
1596 Self::SeatOutOfRange => "seat is outside the configured universe",
1597 Self::CandidateOutOfRange => "candidate is outside the configured universe",
1598 Self::PassComplete => "actuation pass is already complete",
1599 Self::SeatAlreadyAllocated => "seat already has an allocation",
1600 Self::EffectAlreadyApplied => "applied seat score cannot change",
1601 Self::SeatNotAllocated => "seat has no allocation",
1602 Self::SeatAlreadyActuated => "seat allocation is already actuated",
1603 Self::PassNotReady => "actuation pass still has unapplied allocations",
1604});