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 total_order(&self) -> bool {
74 self.history@.len() == self.pc
75 }
76
77 pub open spec fn active_before_done(&self) -> bool {
79 self.active ==> self.pc < self.steps
80 }
81
82 pub open spec fn inv(&self) -> bool {
84 self.type_invariant() && self.total_order() && self.active_before_done()
85 }
86
87 pub fn new(steps: usize, value_domain_size: u64, initial_value: u64) -> (s: Sequential)
89 requires
90 steps > 0,
91 value_domain_size > 0,
92 initial_value < value_domain_size,
93 ensures
94 s.steps == steps,
95 s.value_domain_size == value_domain_size,
96 s.pc == 0,
97 s.value == initial_value,
98 !s.active,
99 s.history@.len() == 0,
100 s.inv(),
101 {
102 Sequential {
103 steps,
104 value_domain_size,
105 pc: 0,
106 value: initial_value,
107 active: false,
108 history: Vec::new(),
109 }
110 }
111
112 pub fn begin_step(&mut self) -> (accepted: bool)
114 requires old(self).inv(),
115 ensures
116 accepted == (old(self).pc < old(self).steps && !old(self).active),
117 begin_step_action(
118 old(self).pc as nat,
119 final(self).pc as nat,
120 old(self).steps as nat,
121 !old(self).active,
122 accepted,
123 ),
124 crate::connectives::marker::set_if(
125 old(self).active,
126 final(self).active,
127 accepted,
128 ),
129 final(self).steps == old(self).steps,
130 final(self).value_domain_size == old(self).value_domain_size,
131 if accepted {
132 final(self).active
133 && final(self).pc == old(self).pc
134 && final(self).value == old(self).value
135 && final(self).history@ == old(self).history@
136 } else {
137 final(self).pc == old(self).pc
138 && final(self).value == old(self).value
139 && final(self).active == old(self).active
140 && final(self).history@ == old(self).history@
141 },
142 final(self).inv(),
143 {
144 if self.pc < self.steps && !self.active {
145 self.active = true;
146 true
147 } else {
148 false
149 }
150 }
151
152 #[expect(clippy::arithmetic_side_effects, reason = "Verus proves pc remains within the step bound")]
156 pub fn complete_step(&mut self, next_value: u64) -> (accepted: bool)
157 requires old(self).inv(),
158 ensures
159 accepted == (old(self).active && next_value < old(self).value_domain_size),
160 complete_step_action(
161 old(self).pc as nat,
162 final(self).pc as nat,
163 old(self).steps as nat,
164 old(self).active,
165 next_value < old(self).value_domain_size,
166 accepted,
167 ),
168 crate::connectives::marker::clear_if(
169 old(self).active,
170 final(self).active,
171 accepted,
172 ),
173 final(self).steps == old(self).steps,
174 final(self).value_domain_size == old(self).value_domain_size,
175 if accepted {
176 !final(self).active
177 && final(self).pc == old(self).pc + 1
178 && final(self).value == next_value
179 && final(self).history@ == old(self).history@.push(next_value)
180 } else {
181 final(self).pc == old(self).pc
182 && final(self).value == old(self).value
183 && final(self).active == old(self).active
184 && final(self).history@ == old(self).history@
185 },
186 final(self).inv(),
187 {
188 if self.active && next_value < self.value_domain_size {
189 let ghost old_history = self.history@;
190 self.value = next_value;
191 self.history.push(next_value);
192 self.pc += 1;
193 self.active = false;
194 assert(self.values_valid()) by {
195 assert forall|i: int| 0 <= i < self.history@.len()
196 implies #[trigger] self.history@[i] < self.value_domain_size by {
197 if i < old_history.len() {
198 assert(self.history@[i] == old_history[i]);
199 } else {
200 assert(i == old_history.len());
201 assert(self.history@[i] == next_value);
202 }
203 }
204 }
205 true
206 } else {
207 false
208 }
209 }
210
211 pub fn done_stuttering(&mut self) -> (enabled: bool)
214 requires old(self).inv(),
215 ensures
216 enabled == (old(self).pc == old(self).steps && !old(self).active),
217 final(self).steps == old(self).steps,
218 final(self).value_domain_size == old(self).value_domain_size,
219 final(self).pc == old(self).pc,
220 final(self).value == old(self).value,
221 final(self).active == old(self).active,
222 final(self).history@ == old(self).history@,
223 final(self).inv(),
224 {
225 self.pc == self.steps && !self.active
226 }
227}
228
229}