Skip to main content

automation_structures/
connective_api.rs

1//! Public runtime forms for the canonical connective roles.
2//!
3//! Connectives are intentionally smaller than obligation-bearing structures.
4//! They carry values or express agreement between adjacent structures without
5//! inventing a second domain-specific implementation of the same role.
6
7use vstd::prelude::*;
8
9verus! {
10
11/// An ordered partial result paired with its pending suffix.
12pub struct Accumulator<T: Copy> {
13    #[allow(dead_code)]
14    original: Ghost<Seq<T>>,
15    accumulated: Vec<T>,
16    pending: Vec<T>,
17}
18
19impl<T: Copy> Accumulator<T> {
20    pub closed spec fn well_formed(&self) -> bool {
21        crate::connectives::accumulator::carries(
22            self.original@,
23            self.accumulated@,
24            self.pending@,
25        )
26    }
27
28    /// Construct an accumulator with an empty consumed prefix.
29    pub fn new(values: Vec<T>) -> (accumulator: Self)
30        ensures accumulator.well_formed(),
31    {
32        let ghost original = values@;
33        Self { original: Ghost(original), accumulated: Vec::new(), pending: values }
34    }
35
36    /// Number of values already accumulated.
37    pub fn accumulated_len(&self) -> usize { self.accumulated.len() }
38
39    /// Number of values still pending.
40    pub fn pending_len(&self) -> usize { self.pending.len() }
41
42    /// Whether no values remain pending.
43    pub fn is_complete(&self) -> bool { self.pending.is_empty() }
44
45    /// Read one accumulated value by original order.
46    #[expect(clippy::indexing_slicing, reason = "the branch proves the accumulated index is in bounds")]
47    pub fn accumulated(&self, index: usize) -> Option<T> {
48        if index < self.accumulated.len() { Some(self.accumulated[index]) } else { None }
49    }
50
51    /// Read one pending value by original order.
52    #[expect(clippy::indexing_slicing, reason = "the branch proves the pending index is in bounds")]
53    pub fn pending(&self, index: usize) -> Option<T> {
54        if index < self.pending.len() { Some(self.pending[index]) } else { None }
55    }
56
57    /// Move the next pending value into the accumulated prefix.
58    #[expect(clippy::indexing_slicing, reason = "the nonempty guard proves the pending head exists")]
59    pub fn advance(&mut self) -> (value: Option<T>)
60        requires old(self).well_formed(),
61        ensures final(self).well_formed(),
62    {
63        if self.pending.is_empty() { return None; }
64        let ghost old_accumulated = self.accumulated@;
65        let ghost old_pending = self.pending@;
66        let value = self.pending[0];
67        self.pending.remove(0);
68        self.accumulated.push(value);
69        proof {
70            crate::connectives::accumulator::consume_pending_head(
71                old_accumulated,
72                old_pending,
73            );
74            assert(self.accumulated@ =~= old_accumulated.push(old_pending[0]));
75            assert(self.pending@ =~= old_pending.skip(1));
76        }
77        Some(value)
78    }
79}
80
81/// A bounded FIFO connective.
82pub struct Buffer<T> {
83    capacity: usize,
84    values: Vec<T>,
85}
86
87impl<T> Buffer<T> {
88    pub closed spec fn well_formed(&self) -> bool {
89        crate::connectives::buffer::buffer_bounded(self.values@, self.capacity as nat)
90    }
91
92    /// Construct an empty FIFO with a fixed capacity.
93    pub fn new(capacity: usize) -> (buffer: Self)
94        ensures buffer.well_formed(),
95    {
96        Self { capacity, values: Vec::new() }
97    }
98
99    /// Fixed FIFO capacity.
100    pub fn capacity(&self) -> usize { self.capacity }
101
102    /// Number of retained values.
103    pub fn len(&self) -> usize { self.values.len() }
104
105    /// Whether no values are retained.
106    pub fn is_empty(&self) -> bool { self.values.is_empty() }
107
108    /// Whether the FIFO is at capacity.
109    pub fn is_full(&self) -> bool { self.values.len() == self.capacity }
110
111    /// Push one value, returning it unchanged when the FIFO is full.
112    pub fn push(&mut self, value: T) -> (result: Result<(), T>)
113        requires old(self).well_formed(),
114        ensures final(self).well_formed(),
115    {
116        if self.values.len() >= self.capacity { return Err(value); }
117        self.values.push(value);
118        Ok(())
119    }
120
121    /// Remove and return the oldest retained value.
122    pub fn pop(&mut self) -> (value: Option<T>)
123        requires old(self).well_formed(),
124        ensures final(self).well_formed(),
125    {
126        if self.values.is_empty() { None } else { Some(self.values.remove(0)) }
127    }
128}
129
130/// A retained nonnegative occurrence or generation count.
131#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
132pub struct Counter {
133    value: u64,
134}
135
136impl Counter {
137    /// Construct a counter at `value`.
138    pub fn new(value: u64) -> (counter: Self) { Self { value } }
139
140    /// Current retained count.
141    pub fn value(&self) -> u64 { self.value }
142
143    /// Increment unless the `u64` representation is exhausted.
144    #[must_use]
145    pub fn try_increment(&mut self) -> (accepted: bool) {
146        if self.value == u64::MAX { return false; }
147        self.value = self.value + 1;
148        true
149    }
150
151    /// Decrement when positive.
152    #[must_use]
153    pub fn try_decrement(&mut self) -> (accepted: bool) {
154        if self.value == 0 { return false; }
155        self.value = self.value - 1;
156        true
157    }
158}
159
160/// A reusable retained boolean marker.
161#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
162pub struct Marker {
163    marked: bool,
164}
165
166impl Marker {
167    /// Construct a marker with an explicit initial state.
168    pub fn new(marked: bool) -> (marker: Self) { Self { marked } }
169
170    /// Whether the marker is set.
171    pub fn is_marked(&self) -> bool { self.marked }
172
173    /// Set the marker and return whether its state changed.
174    pub fn set(&mut self) -> (changed: bool) {
175        let changed = !self.marked;
176        self.marked = true;
177        changed
178    }
179
180    /// Clear the marker and return whether its state changed.
181    pub fn clear(&mut self) -> (changed: bool) {
182        let changed = self.marked;
183        self.marked = false;
184        changed
185    }
186}
187
188/// Test agreement between a projected membership answer and its source.
189pub fn projection_consistent(projected: bool, source: bool) -> (consistent: bool)
190    ensures consistent == crate::connectives::projection::membership_consistent(projected, source),
191{
192    projected == source
193}
194
195/// Test the canonical strict ordering relation between two positions.
196pub fn strictly_before(left: usize, right: usize) -> (ordered: bool) {
197    crate::connectives::ordering_pass::is_strictly_before(left, right)
198}
199
200}
201
202impl<T: Copy + core::fmt::Debug> core::fmt::Debug for Accumulator<T> {
203    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
204        formatter
205            .debug_struct("Accumulator")
206            .field("accumulated", &self.accumulated)
207            .field("pending", &self.pending)
208            .finish()
209    }
210}
211
212impl<T: core::fmt::Debug> core::fmt::Debug for Buffer<T> {
213    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
214        formatter
215            .debug_struct("Buffer")
216            .field("capacity", &self.capacity)
217            .field("values", &self.values)
218            .finish()
219    }
220}