automation_structures/modalities/
sequential.rs1use vstd::prelude::*;
4
5verus! {
6
7pub open spec fn begin_step_action(
9 before_position: nat,
10 after_position: nat,
11 steps: nat,
12 selected: bool,
13 accepted: bool,
14) -> bool {
15 let enabled = selected && before_position < steps;
16 &&& accepted == enabled
17 &&& after_position == before_position
18}
19
20pub open spec fn complete_step_action(
22 before_position: nat,
23 after_position: nat,
24 steps: nat,
25 selected: bool,
26 value_admitted: bool,
27 accepted: bool,
28) -> bool {
29 let enabled = selected
30 && value_admitted
31 && before_position < steps;
32 &&& accepted == enabled
33 &&& after_position == if accepted {
34 before_position + 1
35 } else {
36 before_position
37 }
38}
39
40pub struct Sequential {
42 pub steps: usize,
44 pub value_domain_size: u64,
46 pub pc: usize,
48 pub value: u64,
50 pub active: bool,
52 pub history: Vec<u64>,
54}
55
56impl Sequential {
57 pub open spec fn values_valid(&self) -> bool {
59 forall|i: int| 0 <= i < self.history@.len()
60 ==> #[trigger] self.history@[i] < self.value_domain_size
61 }
62
63 pub open spec fn type_invariant(&self) -> bool {
65 &&& self.steps > 0
66 &&& self.value_domain_size > 0
67 &&& self.pc <= self.steps
68 &&& self.value < self.value_domain_size
69 &&& self.values_valid()
70 }
71
72 pub open spec fn history_position_agreement(&self) -> bool {
74 self.history@.len() == self.pc
75 }
76
77 pub open spec fn total_order(&self) -> bool {
81 self.history_position_agreement()
82 }
83
84 pub open spec fn active_before_done(&self) -> bool {
86 self.active ==> self.pc < self.steps
87 }
88
89 pub open spec fn inv(&self) -> bool {
91 self.type_invariant() && self.history_position_agreement() && self.active_before_done()
92 }
93
94 pub fn new(steps: usize, value_domain_size: u64, initial_value: u64) -> (s: Sequential)
96 requires
97 steps > 0,
98 value_domain_size > 0,
99 initial_value < value_domain_size,
100 ensures
101 s.steps == steps,
102 s.value_domain_size == value_domain_size,
103 s.pc == 0,
104 s.value == initial_value,
105 !s.active,
106 s.history@.len() == 0,
107 s.inv(),
108 {
109 Sequential {
110 steps,
111 value_domain_size,
112 pc: 0,
113 value: initial_value,
114 active: false,
115 history: Vec::new(),
116 }
117 }
118
119 pub fn begin_step(&mut self) -> (accepted: bool)
121 requires old(self).inv(),
122 ensures
123 accepted == (old(self).pc < old(self).steps && !old(self).active),
124 begin_step_action(
125 old(self).pc as nat,
126 final(self).pc as nat,
127 old(self).steps as nat,
128 !old(self).active,
129 accepted,
130 ),
131 crate::connectives::marker::set_if(
132 old(self).active,
133 final(self).active,
134 accepted,
135 ),
136 final(self).steps == old(self).steps,
137 final(self).value_domain_size == old(self).value_domain_size,
138 if accepted {
139 final(self).active
140 && final(self).pc == old(self).pc
141 && final(self).value == old(self).value
142 && final(self).history@ == old(self).history@
143 } else {
144 final(self).pc == old(self).pc
145 && final(self).value == old(self).value
146 && final(self).active == old(self).active
147 && final(self).history@ == old(self).history@
148 },
149 final(self).inv(),
150 {
151 if self.pc < self.steps && !self.active {
152 self.active = true;
153 true
154 } else {
155 false
156 }
157 }
158
159 #[expect(clippy::arithmetic_side_effects, reason = "Verus proves pc remains within the step bound")]
163 pub fn complete_step(&mut self, next_value: u64) -> (accepted: bool)
164 requires old(self).inv(),
165 ensures
166 accepted == (old(self).active && next_value < old(self).value_domain_size),
167 complete_step_action(
168 old(self).pc as nat,
169 final(self).pc as nat,
170 old(self).steps as nat,
171 old(self).active,
172 next_value < old(self).value_domain_size,
173 accepted,
174 ),
175 crate::connectives::marker::clear_if(
176 old(self).active,
177 final(self).active,
178 accepted,
179 ),
180 final(self).steps == old(self).steps,
181 final(self).value_domain_size == old(self).value_domain_size,
182 if accepted {
183 !final(self).active
184 && final(self).pc == old(self).pc + 1
185 && final(self).value == next_value
186 && final(self).history@ == old(self).history@.push(next_value)
187 } else {
188 final(self).pc == old(self).pc
189 && final(self).value == old(self).value
190 && final(self).active == old(self).active
191 && final(self).history@ == old(self).history@
192 },
193 final(self).inv(),
194 {
195 if self.active && next_value < self.value_domain_size {
196 let ghost old_history = self.history@;
197 self.value = next_value;
198 self.history.push(next_value);
199 self.pc += 1;
200 self.active = false;
201 assert(self.values_valid()) by {
202 assert forall|i: int| 0 <= i < self.history@.len()
203 implies #[trigger] self.history@[i] < self.value_domain_size by {
204 if i < old_history.len() {
205 assert(self.history@[i] == old_history[i]);
206 } else {
207 assert(i == old_history.len());
208 assert(self.history@[i] == next_value);
209 }
210 }
211 }
212 true
213 } else {
214 false
215 }
216 }
217
218 pub fn done_stuttering(&mut self) -> (enabled: bool)
221 requires old(self).inv(),
222 ensures
223 enabled == (old(self).pc == old(self).steps && !old(self).active),
224 final(self).steps == old(self).steps,
225 final(self).value_domain_size == old(self).value_domain_size,
226 final(self).pc == old(self).pc,
227 final(self).value == old(self).value,
228 final(self).active == old(self).active,
229 final(self).history@ == old(self).history@,
230 final(self).inv(),
231 {
232 self.pc == self.steps && !self.active
233 }
234}
235
236}