automation_structures/compositions/
reduction.rs1use vstd::prelude::*;
12
13#[allow(unused_imports)]
14use crate::primitives::audit_sink::{AdditiveChain, AuditSink, ChainOperation};
15
16verus! {
17
18pub open spec fn sum_to(s: Seq<u64>, n: int) -> int
20 decreases n,
21{
22 if n <= 0 {
23 0
24 } else if n > s.len() as int {
25 0
26 } else {
27 s[n - 1] as int + sum_to(s, n - 1)
28 }
29}
30
31pub open spec fn sum_spec(s: Seq<u64>) -> int {
33 sum_to(s, s.len() as int)
34}
35
36pub proof fn lemma_sum_to_bounded(s: Seq<u64>, n: int)
38 requires
39 forall|k: int| 0 <= k < s.len() ==> s[k] <= 1_000_000_000u64,
40 0 <= n <= s.len() as int,
41 ensures
42 sum_to(s, n) <= 1_000_000_000 * n,
43 decreases n,
44{
45 if n > 0 {
46 lemma_sum_to_bounded(s, n - 1);
47 }
48}
49
50proof fn lemma_sum_to_push_prefix(s: Seq<u64>, x: u64, n: int)
52 requires
53 0 <= n <= s.len() as int,
54 ensures
55 sum_to(s.push(x), n) == sum_to(s, n),
56 decreases n,
57{
58 if n > 0 {
59 lemma_sum_to_push_prefix(s, x, n - 1);
60 assert(s.push(x)[n - 1] == s[n - 1]);
61 }
62}
63
64pub proof fn lemma_sum_push(s: Seq<u64>, x: u64)
67 ensures
68 sum_spec(s.push(x)) == sum_spec(s) + x as int,
69{
70 let t = s.push(x);
71 let n = s.len() as int;
72 assert(t.len() == n + 1);
73 lemma_sum_to_push_prefix(s, x, n);
74 assert(t[n] == x);
75 assert(sum_to(t, n + 1) == t[n] as int + sum_to(t, n));
76}
77
78pub struct Reducer {
80 pub source: Vec<u64>,
82 pub audit: AuditSink<AdditiveChain>,
84}
85
86impl Reducer {
87 pub open spec fn prefix_binding(&self) -> bool {
89 &&& self.audit.log.len() <= self.source.len()
90 &&& forall|index: int|
91 #![trigger self.audit.log@[index]]
92 0 <= index < self.audit.log.len() ==>
93 self.audit.log@[index].operation == self.source@[index]
94 }
95
96 pub open spec fn aggregate(&self) -> bool {
98 self.audit.last_hash as int == sum_to(self.source@, self.audit.log.len() as int)
99 }
100
101 pub open spec fn bounded(&self) -> bool {
103 &&& forall|k: int| 0 <= k < self.source@.len() ==> self.source@[k] <= 1_000_000_000u64
104 &&& self.source@.len() <= 1_000_000_000
105 }
106
107 pub open spec fn inv(&self) -> bool {
109 &&& self.audit.inv()
110 &&& self.audit.max_log_len == self.source.len()
111 &&& self.prefix_binding()
112 &&& self.aggregate()
113 &&& self.bounded()
114 }
115
116 pub fn new(items: Vec<u64>) -> (r: Reducer)
118 requires
119 forall|k: int| 0 <= k < items@.len() ==> items@[k] <= 1_000_000_000u64,
120 items@.len() <= 1_000_000_000,
121 ensures
122 r.inv(),
123 r.source@ == items@,
124 r.audit.log@.len() == 0,
125 r.audit.last_hash == 0,
126 {
127 let audit = AuditSink::with_operator(items.len(), AdditiveChain);
128 let r = Reducer { source: items, audit };
129 proof {
130 assert(r.audit.log@.len() == 0);
131 assert(sum_to(r.source@, 0) == 0);
132 }
133 r
134 }
135
136 pub fn position(&self) -> (position: usize)
138 ensures position == self.audit.log@.len(),
139 {
140 self.audit.log.len()
141 }
142
143 pub fn result(&self) -> (result: u64)
145 ensures result == self.audit.last_hash,
146 {
147 self.audit.last_hash
148 }
149
150 pub fn remaining_len(&self) -> (remaining: usize)
152 requires self.prefix_binding(),
153 ensures remaining == self.source@.len() - self.audit.log@.len(),
154 {
155 self.source.len() - self.audit.log.len()
156 }
157
158 pub fn done(&self) -> (d: bool)
160 requires self.prefix_binding(),
161 ensures
162 d == (self.audit.log@.len() == self.source@.len()),
163 {
164 self.audit.log.len() == self.source.len()
165 }
166
167 pub fn process(&mut self)
169 requires
170 old(self).inv(),
171 old(self).audit.log@.len() < old(self).source@.len(),
172 ensures
173 final(self).inv(),
174 final(self).source@ == old(self).source@,
175 final(self).audit.log@.len() == old(self).audit.log@.len() + 1,
176 final(self).audit.last_hash
177 == old(self).audit.last_hash
178 + old(self).source@[old(self).audit.log@.len() as int],
179 {
180 let old_position = self.audit.log.len();
181 let x = self.source[old_position];
182 let ghost old_log = self.audit.log@;
183 let ghost source = self.source@;
184 proof {
185 lemma_sum_to_bounded(self.source@, old_position as int);
186 assert(self.audit.last_hash as int
187 == sum_to(self.source@, old_position as int));
188 assert(self.audit.last_hash as int <= 1_000_000_000 * old_position as int);
189 assert(x <= 1_000_000_000u64);
190 assert(old_position < 1_000_000_000usize);
191 assert(self.audit.last_hash as int + x as int <= 1_000_000_000_000_000_000int);
192 assert(1_000_000_000_000_000_000int < u64::MAX as int);
193 assert(self.audit.operator.enabled(self.audit.last_hash, x));
194 }
195 let accepted = self.audit.record(x);
196 assert(accepted);
197 let _ = accepted;
198
199 proof {
200 assert(self.audit.log@[old_position as int].operation == x);
201 assert(self.prefix_binding()) by {
202 assert forall|index: int|
203 #![trigger self.audit.log@[index]]
204 0 <= index < self.audit.log.len() implies
205 self.audit.log@[index].operation == self.source@[index] by {
206 if index < old_position {
207 assert(self.audit.log@[index] == old_log[index]);
208 } else {
209 assert(index == old_position);
210 }
211 }
212 }
213 assert(sum_to(source, old_position as int + 1)
214 == source[old_position as int] as int
215 + sum_to(source, old_position as int));
216 assert(self.aggregate());
217 }
218 }
219}
220
221pub open spec fn fold_to(s: Seq<u64>, n: int, identity: u64, op: spec_fn(u64, u64) -> u64) -> u64
234 decreases n,
235{
236 if n <= 0 {
237 identity
238 } else if n > s.len() as int {
239 identity
240 } else {
241 op(fold_to(s, n - 1, identity, op), s[n - 1])
242 }
243}
244
245proof fn lemma_fold_to_bounded_sum(s: Seq<u64>, n: int)
250 requires
251 forall|k: int| 0 <= k < s.len() ==> s[k] <= 1_000_000_000u64,
252 0 <= n <= s.len() as int,
253 ensures
254 fold_to(s, n, 0, |a: u64, b: u64| (a + b) as u64) <= 1_000_000_000 * n,
255 decreases n,
256{
257 if n > 0 {
258 lemma_fold_to_bounded_sum(s, n - 1);
259 }
260}
261
262pub fn reduce_sum(items: &[u64]) -> (result: u64)
266 requires
267 forall|k: int| 0 <= k < items@.len() ==> items@[k] <= 1_000_000_000u64,
268 items@.len() <= 1_000_000_000,
269 ensures
270 result as int == fold_to(items@, items@.len() as int, 0, |a: u64, b: u64| (a + b) as u64) as int,
271{
272 let n: usize = items.len();
273 let mut result: u64 = 0;
274 let mut i: usize = 0;
275 while i < n
276 invariant
277 i <= n,
278 n == items@.len(),
279 n <= 1_000_000_000,
280 forall|k: int| 0 <= k < items@.len() ==> items@[k] <= 1_000_000_000u64,
281 result as int == fold_to(items@, i as int, 0, |a: u64, b: u64| (a + b) as u64) as int,
282 decreases n - i,
283 {
284 proof {
285 lemma_fold_to_bounded_sum(items@, i as int);
286 }
287 result = result + items[i];
288 i = i + 1;
289 }
290 result
291}
292
293pub fn reduce_max(items: &[u64]) -> (result: u64)
295 requires
296 forall|k: int| 0 <= k < items@.len() ==> items@[k] <= 1_000_000_000u64,
297 items@.len() <= 1_000_000_000,
298 ensures
299 result as int == fold_to(items@, items@.len() as int, 0, |a: u64, b: u64| if a > b { a } else { b }) as int,
300{
301 let n: usize = items.len();
302 let mut result: u64 = 0;
303 let mut i: usize = 0;
304 while i < n
305 invariant
306 i <= n,
307 n == items@.len(),
308 forall|k: int| 0 <= k < items@.len() ==> items@[k] <= 1_000_000_000u64,
309 result <= 1_000_000_000u64,
310 result as int == fold_to(items@, i as int, 0, |a: u64, b: u64| if a > b { a } else { b }) as int,
311 decreases n - i,
312 {
313 if items[i] > result {
314 result = items[i];
315 }
316 i = i + 1;
317 }
318 result
319}
320
321}