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 {
255 inner: BisectionCarrier,
256}
257
258impl Bisection {
259 #[verifier::type_invariant]
260 closed spec fn well_formed(&self) -> bool { self.inner.invariant() }
261
262 pub fn new(domain_size: u64, threshold: u64) -> (result: Result<Self, BisectionBuildError>) {
269 if domain_size < 2 { return Err(BisectionBuildError::DomainTooSmall); }
270 if threshold < 1 || threshold >= domain_size {
271 return Err(BisectionBuildError::ThresholdOutOfRange);
272 }
273 proof { lemma_u64_domain_fits_64(domain_size); }
274 let inner = BisectionCarrier::new(0, domain_size, threshold, domain_size, 64);
275 Ok(Self { inner })
276 }
277
278 pub fn lower(&self) -> u64 { self.inner.lo }
280
281 pub fn upper(&self) -> u64 { self.inner.hi }
283
284 pub fn threshold(&self) -> u64 { self.inner.threshold }
286
287 pub fn probes_taken(&self) -> u64 { self.inner.budget.allocated }
289
290 pub fn max_probes(&self) -> u64 { self.inner.budget.capacity }
292
293 pub fn is_converged(&self) -> bool {
295 proof { use_type_invariant(&*self); }
296 self.inner.converged()
297 }
298
299 pub fn probe(&mut self) -> (result: Result<(), BisectionError>) {
305 proof { use_type_invariant(&*self); }
306 if self.inner.hi - self.inner.lo < 2 { return Err(BisectionError::AlreadyConverged); }
307 let mut carrier = bisection_sentinel();
308 core::mem::swap(&mut self.inner, &mut carrier);
309 carrier.probe();
310 core::mem::swap(&mut self.inner, &mut carrier);
311 Ok(())
312 }
313
314 pub fn converge(&mut self) {
316 proof { use_type_invariant(&*self); }
317 let mut carrier = bisection_sentinel();
318 core::mem::swap(&mut self.inner, &mut carrier);
319 carrier.bisect();
320 core::mem::swap(&mut self.inner, &mut carrier);
321 }
322}
323
324#[derive(Clone, Copy, Debug, Eq, PartialEq)]
326#[non_exhaustive]
327pub enum EquivalenceClassError {
328 ElementOutOfRange,
330}
331
332pub struct EquivalenceClass {
345 inner: EquivalenceClassCarrier,
346}
347
348impl EquivalenceClass {
349 #[verifier::type_invariant]
350 closed spec fn well_formed(&self) -> bool { self.inner.inv() }
351
352 pub fn new(elements: usize, max_unions: u64) -> (classes: Self) {
354 Self { inner: EquivalenceClassCarrier::new(elements, max_unions) }
355 }
356
357 pub fn len(&self) -> usize { self.inner.n }
359
360 pub fn is_empty(&self) -> bool { self.inner.n == 0 }
362
363 pub fn unions_performed(&self) -> u64 { self.inner.budget.allocated }
365
366 pub fn max_unions(&self) -> u64 { self.inner.budget.capacity }
368
369 pub fn representative(&self, element: usize) -> (result: Result<usize, EquivalenceClassError>) {
375 proof { use_type_invariant(&*self); }
376 if element >= self.inner.n { return Err(EquivalenceClassError::ElementOutOfRange); }
377 Ok(self.inner.find(element))
378 }
379
380 pub fn union(&mut self, left: usize, right: usize) -> (result: Result<bool, EquivalenceClassError>) {
386 proof { use_type_invariant(&*self); }
387 if left >= self.inner.n || right >= self.inner.n {
388 return Err(EquivalenceClassError::ElementOutOfRange);
389 }
390 let mut carrier = equivalence_class_sentinel();
391 core::mem::swap(&mut self.inner, &mut carrier);
392 let merged = carrier.union(left, right);
393 core::mem::swap(&mut self.inner, &mut carrier);
394 Ok(merged)
395 }
396
397 pub fn equivalent(&self, left: usize, right: usize) -> (result: Result<bool, EquivalenceClassError>) {
403 proof { use_type_invariant(&*self); }
404 if left >= self.inner.n || right >= self.inner.n {
405 return Err(EquivalenceClassError::ElementOutOfRange);
406 }
407 Ok(self.inner.same(left, right))
408 }
409}
410
411#[derive(Clone, Copy, Debug, Eq, PartialEq)]
413#[non_exhaustive]
414pub enum RateLimitBuildError {
415 ZeroLimit,
417 ZeroWindowDuration,
419}
420
421#[derive(Clone, Copy, Debug, Eq, PartialEq)]
423#[non_exhaustive]
424pub enum RateLimitError {
425 ClockExhausted,
427}
428
429pub struct RateLimit {
442 inner: RateLimitCarrier,
443}
444
445impl RateLimit {
446 #[verifier::type_invariant]
447 closed spec fn well_formed(&self) -> bool {
448 self.inner.type_invariant()
449 && self.inner.window_start_not_future()
450 && self.inner.window_duration > 0
451 }
452
453 pub fn new(max_per_window: u64, window_duration: u64, max_clock: u64)
460 -> (result: Result<Self, RateLimitBuildError>) {
461 if max_per_window == 0 { return Err(RateLimitBuildError::ZeroLimit); }
462 if window_duration == 0 { return Err(RateLimitBuildError::ZeroWindowDuration); }
463 Ok(Self { inner: RateLimitCarrier::new(max_per_window, window_duration, max_clock) })
464 }
465
466 pub fn max_per_window(&self) -> u64 { self.inner.budget.capacity }
468
469 pub fn window_duration(&self) -> u64 { self.inner.window_duration }
471
472 pub fn count(&self) -> u64 { self.inner.budget.allocated }
474
475 pub fn clock(&self) -> u64 { self.inner.clock }
477
478 pub fn window_start(&self) -> u64 { self.inner.window_start }
480
481 #[must_use]
483 pub fn try_acquire(&mut self) -> (accepted: bool) {
484 proof { use_type_invariant(&*self); }
485 let mut carrier = rate_limit_sentinel();
486 core::mem::swap(&mut self.inner, &mut carrier);
487 let accepted = carrier.try_acquire();
488 core::mem::swap(&mut self.inner, &mut carrier);
489 accepted
490 }
491
492 pub fn tick(&mut self) -> (result: Result<(), RateLimitError>) {
498 proof { use_type_invariant(&*self); }
499 if self.inner.clock >= self.inner.max_clock { return Err(RateLimitError::ClockExhausted); }
500 let mut carrier = rate_limit_sentinel();
501 core::mem::swap(&mut self.inner, &mut carrier);
502 carrier.tick();
503 core::mem::swap(&mut self.inner, &mut carrier);
504 Ok(())
505 }
506}
507
508#[derive(Clone, Copy, Debug, Eq, PartialEq)]
510#[non_exhaustive]
511pub enum ReductionBuildError {
512 TooManyItems,
514 ValueOutOfRange,
516}
517
518#[derive(Clone, Copy, Debug, Eq, PartialEq)]
520#[non_exhaustive]
521pub enum ReductionError {
522 Complete,
524}
525
526pub struct Reduction {
539 inner: ReductionCarrier,
540}
541
542impl Reduction {
543 #[verifier::type_invariant]
544 closed spec fn well_formed(&self) -> bool {
545 self.inner.inv()
546 }
547
548 pub fn new(items: Vec<u64>) -> (result: Result<Self, ReductionBuildError>) {
554 if items.len() > 1_000_000_000 { return Err(ReductionBuildError::TooManyItems); }
555 if !values_within_max(&items, 1_000_000_000) {
556 return Err(ReductionBuildError::ValueOutOfRange);
557 }
558 Ok(Self { inner: ReductionCarrier::new(items) })
559 }
560
561 pub fn result(&self) -> u64 { self.inner.result() }
563
564 pub fn processed_len(&self) -> usize { self.inner.position() }
566
567 pub fn remaining_len(&self) -> usize {
569 proof { use_type_invariant(&*self); }
570 self.inner.remaining_len()
571 }
572
573 pub fn is_complete(&self) -> bool {
575 proof { use_type_invariant(&*self); }
576 self.inner.done()
577 }
578
579 pub fn process_next(&mut self) -> (result: Result<(), ReductionError>) {
585 proof { use_type_invariant(&*self); }
586 if self.inner.done() { return Err(ReductionError::Complete); }
587 let mut carrier = reduction_sentinel();
588 core::mem::swap(&mut self.inner, &mut carrier);
589 carrier.process();
590 core::mem::swap(&mut self.inner, &mut carrier);
591 Ok(())
592 }
593}
594
595#[derive(Clone, Copy, Debug, Eq, PartialEq)]
597#[non_exhaustive]
598pub enum RelationshipGraphError {
599 NodeOutOfRange,
601 WeightOutOfRange,
603 SelfLoop,
605}
606
607pub struct RelationshipGraph {
620 inner: RelationshipGraphCarrier,
621}
622
623impl RelationshipGraph {
624 #[verifier::type_invariant]
625 closed spec fn well_formed(&self) -> bool { self.inner.inv() }
626
627 pub fn new(num_nodes: usize, max_weight: u64) -> (graph: Self) {
629 Self { inner: RelationshipGraphCarrier::new(num_nodes, max_weight) }
630 }
631
632 pub fn num_nodes(&self) -> usize { self.inner.num_nodes }
634
635 pub fn max_weight(&self) -> u64 { self.inner.max_weight }
637
638 pub fn edge_count(&self) -> usize { self.inner.registry.entries.len() }
640
641 pub fn edge(&self, index: usize) -> Option<(usize, usize, u64)> {
643 if index < self.inner.registry.entries.len() {
644 Some(self.inner.registry.entries[index].0)
645 } else {
646 None
647 }
648 }
649
650 pub fn contains(&self, source: usize, destination: usize) -> bool {
652 proof { use_type_invariant(&*self); }
653 self.inner.contains_pair(source, destination)
654 }
655
656 pub fn add_edge(&mut self, source: usize, destination: usize, weight: u64)
662 -> (result: Result<bool, RelationshipGraphError>) {
663 proof { use_type_invariant(&*self); }
664 if source >= self.inner.num_nodes || destination >= self.inner.num_nodes {
665 return Err(RelationshipGraphError::NodeOutOfRange);
666 }
667 if weight > self.inner.max_weight { return Err(RelationshipGraphError::WeightOutOfRange); }
668 if source == destination { return Err(RelationshipGraphError::SelfLoop); }
669 let mut carrier = relationship_graph_sentinel();
670 core::mem::swap(&mut self.inner, &mut carrier);
671 let added = carrier.add_edge(source, destination, weight);
672 core::mem::swap(&mut self.inner, &mut carrier);
673 Ok(added)
674 }
675
676 pub fn remove_edges(&mut self, source: usize, destination: usize) {
678 proof { use_type_invariant(&*self); }
679 let mut carrier = relationship_graph_sentinel();
680 core::mem::swap(&mut self.inner, &mut carrier);
681 carrier.remove_edge(source, destination);
682 core::mem::swap(&mut self.inner, &mut carrier);
683 }
684}
685
686#[derive(Clone, Copy, Debug, Eq, PartialEq)]
688#[non_exhaustive]
689pub enum SamplerError {
690 ItemOutOfRange,
692 SampleFull,
694 OutsideSupport,
696 AlreadySelected,
698}
699
700pub struct Sampler {
713 inner: SamplerCarrier,
714}
715
716impl Sampler {
717 #[verifier::type_invariant]
718 closed spec fn well_formed(&self) -> bool { self.inner.inv() }
719
720 pub fn new(distribution: Vec<u64>, sample_size: usize) -> (sampler: Self) {
722 Self { inner: SamplerCarrier::new(distribution, sample_size) }
723 }
724
725 pub fn len(&self) -> usize { self.inner.actuation.num_seats }
727
728 pub fn is_empty(&self) -> bool { self.inner.actuation.num_seats == 0 }
730
731 pub fn sample_size(&self) -> usize { self.inner.budget.capacity as usize }
733
734 pub fn selected_len(&self) -> usize { self.inner.budget.allocated as usize }
736
737 pub fn weight(&self, item: usize) -> Option<u64> {
739 proof { use_type_invariant(&*self); }
740 if item < self.inner.actuation.num_seats { Some(self.inner.weight(item)) } else { None }
741 }
742
743 pub fn contains(&self, item: usize) -> bool {
745 proof { use_type_invariant(&*self); }
746 self.inner.contains_exec(item)
747 }
748
749 pub fn sample(&mut self, item: usize) -> (result: Result<(), SamplerError>) {
756 proof { use_type_invariant(&*self); }
757 if item >= self.inner.actuation.num_seats { return Err(SamplerError::ItemOutOfRange); }
758 if self.inner.budget.allocated >= self.inner.budget.capacity {
759 return Err(SamplerError::SampleFull);
760 }
761 if self.inner.weight(item) == 0 { return Err(SamplerError::OutsideSupport); }
762 if self.inner.contains_exec(item) { return Err(SamplerError::AlreadySelected); }
763 let mut carrier = sampler_sentinel();
764 core::mem::swap(&mut self.inner, &mut carrier);
765 carrier.sample(item);
766 core::mem::swap(&mut self.inner, &mut carrier);
767 Ok(())
768 }
769
770 #[must_use]
772 pub fn zero(&mut self, item: usize) -> (accepted: bool) {
773 proof { use_type_invariant(&*self); }
774 let mut carrier = sampler_sentinel();
775 core::mem::swap(&mut self.inner, &mut carrier);
776 let accepted = carrier.zero(item);
777 core::mem::swap(&mut self.inner, &mut carrier);
778 accepted
779 }
780
781 pub fn draw_weighted(&mut self, item: usize, entropy: u64) -> (result: Result<bool, SamplerError>) {
787 proof { use_type_invariant(&*self); }
788 if item >= self.inner.actuation.num_seats { return Err(SamplerError::ItemOutOfRange); }
789 let mut carrier = sampler_sentinel();
790 core::mem::swap(&mut self.inner, &mut carrier);
791 let accepted = carrier.draw_weighted(item, entropy);
792 core::mem::swap(&mut self.inner, &mut carrier);
793 Ok(accepted)
794 }
795
796 pub fn draw_uniform(&mut self, item: usize) -> (result: Result<bool, SamplerError>) {
802 proof { use_type_invariant(&*self); }
803 if item >= self.inner.actuation.num_seats { return Err(SamplerError::ItemOutOfRange); }
804 let mut carrier = sampler_sentinel();
805 core::mem::swap(&mut self.inner, &mut carrier);
806 let accepted = carrier.draw_uniform(item);
807 core::mem::swap(&mut self.inner, &mut carrier);
808 Ok(accepted)
809 }
810}
811
812#[derive(Clone, Copy, Debug, Eq, PartialEq)]
814#[non_exhaustive]
815pub enum SignalBuildError {
816 InitialValueOutOfRange,
818}
819
820#[derive(Clone, Copy, Debug, Eq, PartialEq)]
822#[non_exhaustive]
823pub enum SignalError {
824 ValueOutOfRange,
826 ListenerOutOfRange,
828 ListenerNotPending,
830 ChangeCapacityExhausted,
832}
833
834pub struct Signal {
848 inner: SignalCarrier,
849}
850
851impl Signal {
852 #[verifier::type_invariant]
853 closed spec fn well_formed(&self) -> bool {
854 self.inner.inv()
855 }
856
857 pub fn new(initial_value: u64, num_values: u64, num_listeners: usize)
864 -> (result: Result<Self, SignalBuildError>) {
865 Self::with_change_capacity(initial_value, num_values, num_listeners, usize::MAX)
866 }
867
868 pub fn with_change_capacity(
875 initial_value: u64,
876 num_values: u64,
877 num_listeners: usize,
878 max_changes: usize,
879 ) -> (result: Result<Self, SignalBuildError>) {
880 if initial_value >= num_values { return Err(SignalBuildError::InitialValueOutOfRange); }
881 Ok(Self { inner: SignalCarrier::new(initial_value, num_values, num_listeners, max_changes) })
882 }
883
884 pub fn value(&self) -> u64 {
886 proof { use_type_invariant(&*self); }
887 self.inner.current_value()
888 }
889
890 pub fn listener_count(&self) -> usize { self.inner.num_listeners }
892
893 pub fn change_observed(&self) -> bool { !self.inner.audit.log.is_empty() }
895
896 pub fn change_capacity(&self) -> usize { self.inner.audit.max_log_len }
898
899 pub fn change_count(&self) -> usize { self.inner.audit.log.len() }
901
902 pub fn is_pending(&self, listener: usize) -> Option<bool> {
904 proof { use_type_invariant(&*self); }
905 if listener < self.inner.num_listeners { Some(self.inner.is_pending(listener)) } else { None }
906 }
907
908 pub fn is_notified(&self, listener: usize) -> Option<bool> {
910 proof { use_type_invariant(&*self); }
911 if listener < self.inner.num_listeners { Some(self.inner.is_notified(listener)) } else { None }
912 }
913
914 pub fn set_value(&mut self, value: u64) -> (result: Result<bool, SignalError>) {
921 proof { use_type_invariant(&*self); }
922 if value >= self.inner.num_values { return Err(SignalError::ValueOutOfRange); }
923 let current = self.inner.current_value();
924 if value == current { return Ok(false); }
925 if self.inner.audit.log.len() >= self.inner.audit.max_log_len {
926 return Err(SignalError::ChangeCapacityExhausted);
927 }
928 let mut carrier = signal_sentinel();
929 core::mem::swap(&mut self.inner, &mut carrier);
930 carrier.set_value(value);
931 core::mem::swap(&mut self.inner, &mut carrier);
932 Ok(true)
933 }
934
935 pub fn notify(&mut self, listener: usize) -> (result: Result<(), SignalError>) {
941 proof { use_type_invariant(&*self); }
942 if listener >= self.inner.num_listeners { return Err(SignalError::ListenerOutOfRange); }
943 if !self.inner.is_pending(listener) { return Err(SignalError::ListenerNotPending); }
944 let mut carrier = signal_sentinel();
945 core::mem::swap(&mut self.inner, &mut carrier);
946 carrier.notify_listener(listener);
947 core::mem::swap(&mut self.inner, &mut carrier);
948 Ok(())
949 }
950}
951
952#[derive(Clone, Copy, Debug, Eq, PartialEq)]
954#[non_exhaustive]
955pub enum TraversalBuildError {
956 NoNodes,
958 RootOutOfRange,
960}
961
962#[derive(Clone, Copy, Debug, Eq, PartialEq)]
964#[non_exhaustive]
965pub enum TraversalError {
966 NodeOutOfRange,
968 NodeNotQueued,
970 NodeAlreadyVisited,
972 QueueNotEmpty,
974}
975
976pub struct TraversalEngine {
992 inner: TraversalEngineCarrier,
993}
994
995impl TraversalEngine {
996 #[verifier::type_invariant]
997 closed spec fn well_formed(&self) -> bool {
998 self.inner.inv()
999 }
1000
1001 pub fn new(num_nodes: usize, root: usize, budget: u64)
1007 -> (result: Result<Self, TraversalBuildError>) {
1008 if num_nodes == 0 { return Err(TraversalBuildError::NoNodes); }
1009 if root >= num_nodes { return Err(TraversalBuildError::RootOutOfRange); }
1010 Ok(Self { inner: TraversalEngineCarrier::new(num_nodes, root, budget) })
1011 }
1012
1013 pub fn num_nodes(&self) -> usize { self.inner.num_nodes }
1015
1016 pub fn root(&self) -> usize { self.inner.root }
1018
1019 pub fn budget_remaining(&self) -> u64 {
1021 proof { use_type_invariant(&*self); }
1022 self.inner.budget_remaining()
1023 }
1024
1025 pub fn queued_len(&self) -> usize { self.inner.queue.len() }
1027
1028 pub fn visited_len(&self) -> usize { self.inner.visited_count() }
1030
1031 pub fn accepted_len(&self) -> usize { self.inner.accepted.len() }
1033
1034 pub fn accepted_cost(&self) -> u64 { self.inner.budget.allocated }
1036
1037 pub fn is_queued(&self, node: usize) -> bool { self.inner.queue_contains(node) }
1039
1040 pub fn is_visited(&self, node: usize) -> bool { self.inner.visited_contains(node) }
1042
1043 pub fn is_accepted(&self, node: usize) -> bool { self.inner.accepted_contains(node) }
1045
1046 pub fn visit(&mut self, node: usize) -> (result: Result<(), TraversalError>) {
1052 proof { use_type_invariant(&*self); }
1053 if node >= self.inner.num_nodes { return Err(TraversalError::NodeOutOfRange); }
1054 if !self.inner.queue_contains(node) { return Err(TraversalError::NodeNotQueued); }
1055 if self.inner.visited_contains(node) { return Err(TraversalError::NodeAlreadyVisited); }
1056 let mut carrier = traversal_engine_sentinel();
1057 core::mem::swap(&mut self.inner, &mut carrier);
1058 carrier.visit_node(node);
1059 core::mem::swap(&mut self.inner, &mut carrier);
1060 Ok(())
1061 }
1062
1063 pub fn skip(&mut self, node: usize) -> (result: Result<(), TraversalError>) {
1069 proof { use_type_invariant(&*self); }
1070 if node >= self.inner.num_nodes { return Err(TraversalError::NodeOutOfRange); }
1071 if !self.inner.queue_contains(node) { return Err(TraversalError::NodeNotQueued); }
1072 let mut carrier = traversal_engine_sentinel();
1073 core::mem::swap(&mut self.inner, &mut carrier);
1074 carrier.skip(node);
1075 core::mem::swap(&mut self.inner, &mut carrier);
1076 Ok(())
1077 }
1078
1079 pub fn terminate(&mut self) -> (result: Result<(), TraversalError>) {
1085 proof { use_type_invariant(&*self); }
1086 if !self.inner.queue.is_empty() { return Err(TraversalError::QueueNotEmpty); }
1087 let mut carrier = traversal_engine_sentinel();
1088 core::mem::swap(&mut self.inner, &mut carrier);
1089 carrier.terminate();
1090 core::mem::swap(&mut self.inner, &mut carrier);
1091 Ok(())
1092 }
1093}
1094
1095#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1097#[non_exhaustive]
1098pub enum SelectThenActuateBuildError {
1099 NoCandidates,
1101}
1102
1103#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1105#[non_exhaustive]
1106pub enum SelectThenActuateError {
1107 SeatOutOfRange,
1109 CandidateOutOfRange,
1111 PassComplete,
1113 SeatAlreadyAllocated,
1115 EffectAlreadyApplied,
1117 SeatNotAllocated,
1119 SeatAlreadyActuated,
1121 PassNotReady,
1123}
1124
1125pub struct SelectThenActuate {
1141 inner: SelectThenActuateCarrier,
1142}
1143
1144impl SelectThenActuate {
1145 #[verifier::type_invariant]
1146 closed spec fn well_formed(&self) -> bool { self.inner.inv() }
1147
1148 pub fn new(num_seats: usize, num_candidates: usize)
1154 -> (result: Result<Self, SelectThenActuateBuildError>) {
1155 if num_candidates == 0 { return Err(SelectThenActuateBuildError::NoCandidates); }
1156 Ok(Self { inner: SelectThenActuateCarrier::new(num_seats, num_candidates) })
1157 }
1158
1159 pub fn seat_count(&self) -> usize { self.inner.selections.len() }
1161
1162 pub fn candidate_count(&self) -> usize { self.inner.num_candidates }
1164
1165 pub fn score(&self, seat: usize, candidate: usize) -> Option<u64> {
1167 proof { use_type_invariant(&*self); }
1168 if seat >= self.inner.selections.len() || candidate >= self.inner.num_candidates {
1169 None
1170 } else {
1171 Some(self.inner.score_at(seat, candidate))
1172 }
1173 }
1174
1175 pub fn allocation(&self, seat: usize) -> Option<usize> {
1177 proof { use_type_invariant(&*self); }
1178 if seat >= self.inner.selections.len() { None } else { self.inner.allocation_at(seat) }
1179 }
1180
1181 pub fn is_actuated(&self, seat: usize) -> Option<bool> {
1183 proof { use_type_invariant(&*self); }
1184 if seat >= self.inner.selections.len() { None } else { Some(self.inner.is_actuated(seat)) }
1185 }
1186
1187 pub fn is_complete(&self) -> bool { self.inner.is_complete() }
1189
1190 pub fn update_score(
1196 &mut self,
1197 seat: usize,
1198 candidate: usize,
1199 value: u64,
1200 ) -> (result: Result<(), SelectThenActuateError>) {
1201 proof { use_type_invariant(&*self); }
1202 if seat >= self.inner.selections.len() {
1203 return Err(SelectThenActuateError::SeatOutOfRange);
1204 }
1205 if candidate >= self.inner.num_candidates {
1206 return Err(SelectThenActuateError::CandidateOutOfRange);
1207 }
1208 if self.inner.actuation.complete { return Err(SelectThenActuateError::PassComplete); }
1209 if self.inner.is_actuated(seat) {
1210 return Err(SelectThenActuateError::EffectAlreadyApplied);
1211 }
1212 let mut carrier = select_then_actuate_sentinel();
1213 core::mem::swap(&mut self.inner, &mut carrier);
1214 carrier.update_score(seat, candidate, value);
1215 core::mem::swap(&mut self.inner, &mut carrier);
1216 Ok(())
1217 }
1218
1219 pub fn evaluate(&mut self, seat: usize) -> (result: Result<usize, SelectThenActuateError>) {
1225 proof { use_type_invariant(&*self); }
1226 if seat >= self.inner.selections.len() {
1227 return Err(SelectThenActuateError::SeatOutOfRange);
1228 }
1229 if self.inner.actuation.complete { return Err(SelectThenActuateError::PassComplete); }
1230 if self.inner.is_allocated(seat) {
1231 return Err(SelectThenActuateError::SeatAlreadyAllocated);
1232 }
1233 let mut carrier = select_then_actuate_sentinel();
1234 core::mem::swap(&mut self.inner, &mut carrier);
1235 carrier.evaluate(seat);
1236 let winner = match carrier.allocation_at(seat) {
1237 Some(candidate) => candidate,
1238 None => {
1239 core::mem::swap(&mut self.inner, &mut carrier);
1240 return Err(SelectThenActuateError::SeatNotAllocated);
1241 },
1242 };
1243 core::mem::swap(&mut self.inner, &mut carrier);
1244 Ok(winner)
1245 }
1246
1247 pub fn actuate(&mut self, seat: usize) -> (result: Result<(), SelectThenActuateError>) {
1254 proof { use_type_invariant(&*self); }
1255 if seat >= self.inner.selections.len() {
1256 return Err(SelectThenActuateError::SeatOutOfRange);
1257 }
1258 if self.inner.actuation.complete { return Err(SelectThenActuateError::PassComplete); }
1259 if !self.inner.is_allocated(seat) {
1260 return Err(SelectThenActuateError::SeatNotAllocated);
1261 }
1262 if self.inner.is_actuated(seat) {
1263 return Err(SelectThenActuateError::SeatAlreadyActuated);
1264 }
1265 let mut carrier = select_then_actuate_sentinel();
1266 core::mem::swap(&mut self.inner, &mut carrier);
1267 carrier.actuate(seat);
1268 core::mem::swap(&mut self.inner, &mut carrier);
1269 Ok(())
1270 }
1271
1272 pub fn finish(&mut self) -> (result: Result<(), SelectThenActuateError>) {
1278 proof { use_type_invariant(&*self); }
1279 if self.inner.actuation.complete { return Err(SelectThenActuateError::PassComplete); }
1280 if !self.inner.can_finish() { return Err(SelectThenActuateError::PassNotReady); }
1281 let mut carrier = select_then_actuate_sentinel();
1282 core::mem::swap(&mut self.inner, &mut carrier);
1283 carrier.finish();
1284 core::mem::swap(&mut self.inner, &mut carrier);
1285 Ok(())
1286 }
1287}
1288
1289proof fn lemma_u64_domain_fits_64(value: u64)
1290 ensures value as int <= crate::compositions::bisection::pow2(64),
1291{
1292 assert(crate::compositions::bisection::pow2(64) == 18_446_744_073_709_551_616int) by (compute);
1293}
1294
1295fn allocation_snapshot_sentinel() -> (carrier: AllocationSnapshotCarrier)
1296 ensures carrier.type_invariant(), carrier.budget_consistency(),
1297{ AllocationSnapshotCarrier::new(0, 0) }
1298
1299fn federated_budget_sentinel() -> (carrier: FederatedBudgetCarrier)
1300 ensures carrier.inv(),
1301{ FederatedBudgetCarrier::new(0, 0) }
1302
1303fn bisection_sentinel() -> (carrier: BisectionCarrier)
1304 ensures carrier.invariant(),
1305{
1306 proof {
1307 assert(crate::compositions::bisection::pow2(1) == 2) by (compute);
1308 }
1309 BisectionCarrier::new(0, 2, 1, 2, 1)
1310}
1311
1312fn equivalence_class_sentinel() -> (carrier: EquivalenceClassCarrier)
1313 ensures carrier.inv(),
1314{ EquivalenceClassCarrier::new(0, 0) }
1315
1316fn rate_limit_sentinel() -> (carrier: RateLimitCarrier)
1317 ensures carrier.type_invariant(), carrier.window_start_not_future(),
1318 carrier.window_duration > 0,
1319{ RateLimitCarrier::new(1, 1, 0) }
1320
1321fn reduction_sentinel() -> (carrier: ReductionCarrier)
1322 ensures carrier.inv(),
1323{
1324 let values: Vec<u64> = Vec::new();
1325 ReductionCarrier::new(values)
1326}
1327
1328fn relationship_graph_sentinel() -> (carrier: RelationshipGraphCarrier)
1329 ensures carrier.inv(),
1330{ RelationshipGraphCarrier::new(0, 0) }
1331
1332fn sampler_sentinel() -> (carrier: SamplerCarrier)
1333 ensures carrier.inv(),
1334{
1335 let distribution: Vec<u64> = Vec::new();
1336 SamplerCarrier::new(distribution, 0)
1337}
1338
1339fn signal_sentinel() -> (carrier: SignalCarrier)
1340 ensures carrier.inv(),
1341{ SignalCarrier::new(0, 1, 0, 0) }
1342
1343fn traversal_engine_sentinel() -> (carrier: TraversalEngineCarrier)
1344 ensures carrier.inv(),
1345{ TraversalEngineCarrier::new(1, 0, 0) }
1346
1347fn select_then_actuate_sentinel() -> (carrier: SelectThenActuateCarrier)
1348 ensures carrier.inv(),
1349{ SelectThenActuateCarrier::new(0, 1) }
1350
1351}
1352
1353impl AllocationSnapshot {
1354 pub fn accepted_entries(&self) -> impl ExactSizeIterator<Item = &(u64, u64)> {
1356 self.inner.registry.entries.iter()
1357 }
1358}
1359
1360impl FederatedBudget {
1361 pub fn master_available(&self) -> u64 {
1363 self.inner.master.available()
1364 }
1365
1366 pub fn pools(&self) -> impl ExactSizeIterator<Item = (u64, u64)> + '_ {
1368 self.inner
1369 .sub_pools
1370 .iter()
1371 .map(|pool| (pool.allocated + pool.reserved, pool.allocated))
1372 }
1373}
1374
1375impl EquivalenceClass {
1376 pub fn representatives(&self) -> impl ExactSizeIterator<Item = (usize, usize)> + '_ {
1378 (0..self.len()).map(|element| (element, self.inner.find(element)))
1379 }
1380}
1381
1382impl RateLimit {
1383 pub fn max_clock(&self) -> u64 {
1385 self.inner.max_clock
1386 }
1387
1388 pub fn available(&self) -> u64 {
1390 self.inner.budget.available()
1391 }
1392}
1393
1394impl Reduction {
1395 pub fn items(&self) -> &[u64] {
1397 self.inner.source.as_slice()
1398 }
1399
1400 pub fn remaining(&self) -> &[u64] {
1402 &self.inner.source[self.processed_len()..]
1403 }
1404}
1405
1406impl RelationshipGraph {
1407 pub fn edges(&self) -> impl ExactSizeIterator<Item = (usize, usize, u64)> + '_ {
1409 self.inner.registry.entries.iter().map(|entry| entry.0)
1410 }
1411}
1412
1413impl Sampler {
1414 pub fn weights(&self) -> impl ExactSizeIterator<Item = u64> + '_ {
1416 self.inner
1417 .actuation
1418 .allocation
1419 .iter()
1420 .map(|entry| entry.unwrap_or(0))
1421 }
1422
1423 pub fn selected(&self) -> impl Iterator<Item = usize> + '_ {
1425 self.inner
1426 .actuation
1427 .effects
1428 .iter()
1429 .enumerate()
1430 .filter_map(|(item, effect)| effect.is_some().then_some(item))
1431 }
1432}
1433
1434impl Signal {
1435 pub fn value_domain_size(&self) -> u64 {
1437 self.inner.num_values
1438 }
1439
1440 pub fn listeners(&self) -> impl ExactSizeIterator<Item = (bool, bool)> + '_ {
1442 (0..self.listener_count()).map(|listener| {
1443 (
1444 self.inner.is_pending(listener),
1445 self.inner.is_notified(listener),
1446 )
1447 })
1448 }
1449}
1450
1451impl TraversalEngine {
1452 pub fn queued(&self) -> impl ExactSizeIterator<Item = &usize> {
1454 self.inner.queue.values.iter()
1455 }
1456
1457 pub fn visited(&self) -> impl Iterator<Item = usize> + '_ {
1459 self.inner
1460 .visited
1461 .iter()
1462 .enumerate()
1463 .filter_map(|(node, marker)| marker.marked.then_some(node))
1464 }
1465
1466 pub fn accepted(&self) -> impl ExactSizeIterator<Item = &usize> {
1468 self.inner.accepted.accumulated.iter()
1469 }
1470}
1471
1472impl SelectThenActuate {
1473 pub fn scores(&self, seat: usize) -> Option<&[u64]> {
1475 self.inner
1476 .selections
1477 .get(seat)
1478 .map(|selection| selection.scores.as_slice())
1479 }
1480
1481 pub fn allocations(&self) -> impl ExactSizeIterator<Item = Option<usize>> + '_ {
1483 self.inner
1484 .selections
1485 .iter()
1486 .map(|selection| selection.allocation)
1487 }
1488
1489 pub fn effects(&self) -> &[Option<u64>] {
1491 self.inner.actuation.effects.as_slice()
1492 }
1493}
1494
1495impl_observational_debug!(AllocationSnapshot, "AllocationSnapshot",
1496 "capacity" => capacity,
1497 "num_nodes" => num_nodes,
1498 "total_cost" => total_cost,
1499 "budget_remaining" => budget_remaining,
1500 "len" => len,
1501);
1502impl_observational_debug!(FederatedBudget, "FederatedBudget",
1503 "master_capacity" => master_capacity,
1504 "master_allocated" => master_allocated,
1505 "len" => len,
1506);
1507impl_observational_debug!(Bisection, "Bisection",
1508 "lower" => lower,
1509 "upper" => upper,
1510 "threshold" => threshold,
1511 "probes_taken" => probes_taken,
1512 "max_probes" => max_probes,
1513 "converged" => is_converged,
1514);
1515impl_observational_debug!(EquivalenceClass, "EquivalenceClass",
1516 "len" => len,
1517 "unions_performed" => unions_performed,
1518 "max_unions" => max_unions,
1519);
1520impl_observational_debug!(RateLimit, "RateLimit",
1521 "max_per_window" => max_per_window,
1522 "window_duration" => window_duration,
1523 "count" => count,
1524 "clock" => clock,
1525 "window_start" => window_start,
1526);
1527impl_observational_debug!(Reduction, "Reduction",
1528 "result" => result,
1529 "processed_len" => processed_len,
1530 "remaining_len" => remaining_len,
1531 "complete" => is_complete,
1532);
1533impl_observational_debug!(RelationshipGraph, "RelationshipGraph",
1534 "num_nodes" => num_nodes,
1535 "max_weight" => max_weight,
1536 "edge_count" => edge_count,
1537);
1538impl_observational_debug!(Sampler, "Sampler",
1539 "len" => len,
1540 "sample_size" => sample_size,
1541 "selected_len" => selected_len,
1542);
1543impl_observational_debug!(Signal, "Signal",
1544 "value" => value,
1545 "listener_count" => listener_count,
1546 "change_observed" => change_observed,
1547);
1548impl_observational_debug!(TraversalEngine, "TraversalEngine",
1549 "num_nodes" => num_nodes,
1550 "root" => root,
1551 "budget_remaining" => budget_remaining,
1552 "queued_len" => queued_len,
1553 "visited_len" => visited_len,
1554 "accepted_len" => accepted_len,
1555 "accepted_cost" => accepted_cost,
1556);
1557impl_observational_debug!(SelectThenActuate, "SelectThenActuate",
1558 "seat_count" => seat_count,
1559 "candidate_count" => candidate_count,
1560 "complete" => is_complete,
1561);
1562
1563impl_public_error!(AllocationSnapshotError, {
1564 Self::NodeOutOfRange => "node is outside the snapshot universe",
1565 Self::NodeAlreadyAccepted => "node is already accepted",
1566 Self::ZeroCost => "accepted node cost must be positive",
1567 Self::InsufficientBudget => "node cost exceeds the remaining budget",
1568});
1569impl_public_error!(BisectionBuildError, {
1570 Self::DomainTooSmall => "bisection domain must contain at least two points",
1571 Self::ThresholdOutOfRange => "bisection threshold is outside the domain",
1572});
1573impl_public_error!(BisectionError, { Self::AlreadyConverged => "bisection is already converged" });
1574impl_public_error!(EquivalenceClassError, { Self::ElementOutOfRange => "element is outside the partition" });
1575impl_public_error!(RateLimitBuildError, {
1576 Self::ZeroLimit => "rate limit must admit at least one operation",
1577 Self::ZeroWindowDuration => "rate-limit window duration must be positive",
1578});
1579impl_public_error!(RateLimitError, { Self::ClockExhausted => "rate-limit logical clock is exhausted" });
1580impl_public_error!(ReductionBuildError, {
1581 Self::TooManyItems => "reduction input exceeds the verified item ceiling",
1582 Self::ValueOutOfRange => "reduction input exceeds the verified value ceiling",
1583});
1584impl_public_error!(ReductionError, { Self::Complete => "reduction is already complete" });
1585impl_public_error!(RelationshipGraphError, {
1586 Self::NodeOutOfRange => "graph node is outside the configured universe",
1587 Self::WeightOutOfRange => "edge weight exceeds the configured maximum",
1588 Self::SelfLoop => "relationship graph does not admit self-loops",
1589});
1590impl_public_error!(SamplerError, {
1591 Self::ItemOutOfRange => "sample item is outside the distribution",
1592 Self::SampleFull => "bounded sample is full",
1593 Self::OutsideSupport => "sample item has zero support weight",
1594 Self::AlreadySelected => "sample item is already selected",
1595});
1596impl_public_error!(SignalBuildError, { Self::InitialValueOutOfRange => "initial signal value is outside its universe" });
1597impl_public_error!(SignalError, {
1598 Self::ValueOutOfRange => "signal value is outside its universe",
1599 Self::ListenerOutOfRange => "listener is outside the signal universe",
1600 Self::ListenerNotPending => "listener has no pending notification",
1601 Self::ChangeCapacityExhausted => "signal change capacity is exhausted",
1602});
1603impl_public_error!(TraversalBuildError, {
1604 Self::NoNodes => "traversal requires at least one node",
1605 Self::RootOutOfRange => "traversal root is outside the node universe",
1606});
1607impl_public_error!(TraversalError, {
1608 Self::NodeOutOfRange => "traversal node is outside the configured universe",
1609 Self::NodeNotQueued => "traversal node is not queued",
1610 Self::NodeAlreadyVisited => "traversal node was already visited",
1611 Self::QueueNotEmpty => "traversal queue is not empty",
1612});
1613impl_public_error!(SelectThenActuateBuildError, {
1614 Self::NoCandidates => "select-then-actuate requires at least one candidate",
1615});
1616impl_public_error!(SelectThenActuateError, {
1617 Self::SeatOutOfRange => "seat is outside the configured universe",
1618 Self::CandidateOutOfRange => "candidate is outside the configured universe",
1619 Self::PassComplete => "actuation pass is already complete",
1620 Self::SeatAlreadyAllocated => "seat already has an allocation",
1621 Self::EffectAlreadyApplied => "applied seat score cannot change",
1622 Self::SeatNotAllocated => "seat has no allocation",
1623 Self::SeatAlreadyActuated => "seat allocation is already actuated",
1624 Self::PassNotReady => "actuation pass still has unapplied allocations",
1625});