1use vstd::prelude::*;
26
27verus! {
28
29pub open spec fn sum_to(s: Seq<u64>, n: int) -> int
33 decreases n,
34{
35 if n <= 0 { 0 } else if n > s.len() as int { 0 } else { s[n - 1] as int + sum_to(s, n - 1) }
36}
37
38pub proof fn lemma_sum_update(s: Seq<u64>, k: int, nv: u64, n: int)
40 requires 0 <= k < n <= s.len(),
41 ensures sum_to(s.update(k, nv), n) == sum_to(s, n) - s[k] as int + nv as int,
42 decreases n,
43{
44 if n == k + 1 {
45 lemma_sum_unaffected(s, k, nv, k);
46 } else {
47 lemma_sum_update(s, k, nv, n - 1);
48 }
49}
50
51pub proof fn lemma_sum_unaffected(s: Seq<u64>, k: int, nv: u64, m: int)
53 requires 0 <= m <= k < s.len(),
54 ensures sum_to(s.update(k, nv), m) == sum_to(s, m),
55 decreases m,
56{
57 if m > 0 {
58 lemma_sum_unaffected(s, k, nv, m - 1);
59 }
60}
61
62pub proof fn lemma_sum_nonneg(s: Seq<u64>, n: int)
64 requires 0 <= n <= s.len(),
65 ensures sum_to(s, n) >= 0,
66 decreases n,
67{
68 if n > 0 {
69 lemma_sum_nonneg(s, n - 1);
70 }
71}
72
73pub proof fn lemma_sum_ge_two(s: Seq<u64>, i: int, j: int, n: int)
75 requires 0 <= i < n <= s.len(), 0 <= j < n, i != j,
76 ensures sum_to(s, n) >= s[i] as int + s[j] as int,
77 decreases n,
78{
79 if n - 1 == i {
80 lemma_sum_ge_one(s, j, n - 1);
81 lemma_sum_nonneg(s, n - 1);
82 } else if n - 1 == j {
83 lemma_sum_ge_one(s, i, n - 1);
84 lemma_sum_nonneg(s, n - 1);
85 } else {
86 lemma_sum_ge_two(s, i, j, n - 1);
87 }
88}
89
90pub proof fn lemma_sum_ge_one(s: Seq<u64>, i: int, n: int)
92 requires 0 <= i < n <= s.len(),
93 ensures sum_to(s, n) >= s[i] as int,
94 decreases n,
95{
96 if n - 1 == i {
97 lemma_sum_nonneg(s, n - 1);
98 } else {
99 lemma_sum_ge_one(s, i, n - 1);
100 }
101}
102
103pub proof fn lemma_sum_prefix_le(s: Seq<u64>, prefix: int, end: int)
105 requires 0 <= prefix <= end <= s.len(),
106 ensures sum_to(s, prefix) <= sum_to(s, end),
107 decreases end - prefix,
108{
109 if prefix < end {
110 lemma_sum_prefix_le(s, prefix, end - 1);
111 assert(sum_to(s, end) == s[end - 1] as int + sum_to(s, end - 1));
112 }
113}
114
115pub proof fn lemma_sum_push_prefix(s: Seq<u64>, v: u64, m: int)
117 requires 0 <= m <= s.len(),
118 ensures sum_to(s.push(v), m) == sum_to(s, m),
119 decreases m,
120{
121 if m > 0 {
122 lemma_sum_push_prefix(s, v, m - 1);
123 }
124}
125
126pub proof fn lemma_sum_zero(s: Seq<u64>, n: int)
128 requires 0 <= n <= s.len(), forall|k: int| 0 <= k < n ==> s[k] == 0,
129 ensures sum_to(s, n) == 0,
130 decreases n,
131{
132 if n > 0 {
133 lemma_sum_zero(s, n - 1);
134 }
135}
136
137pub struct CompetitiveSelectionHard {
141 pub scores: Vec<u64>,
143 pub allocation: Option<usize>,
145}
146
147impl CompetitiveSelectionHard {
148 pub open spec fn allocation_typing(&self) -> bool {
153 self.allocation matches Option::Some(w) ==> w < self.scores.len()
154 }
155
156 pub open spec fn winner_optimality(&self) -> bool {
159 self.allocation matches Option::Some(w) ==>
160 (w < self.scores.len()
161 && forall|c: int| 0 <= c < self.scores.len() ==> #[trigger] self.scores@[c] <= self.scores@[w as int])
162 }
163
164 pub open spec fn winner_tie_break(&self) -> bool {
171 self.allocation matches Option::Some(w) ==>
172 (w < self.scores.len()
173 && forall|c: int| 0 <= c < self.scores.len() ==>
174 (#[trigger] self.scores@[c] == self.scores@[w as int] ==> w <= c))
175 }
176
177 pub open spec fn inv(&self) -> bool {
179 self.allocation_typing() && self.winner_optimality() && self.winner_tie_break()
180 }
181
182 pub fn new(num_candidates: usize) -> (h: CompetitiveSelectionHard)
184 ensures
185 h.scores@.len() == num_candidates,
186 forall|c: int| 0 <= c < num_candidates ==> h.scores@[c] == 0,
187 h.allocation is None,
188 h.inv(),
189 {
190 let mut scores: Vec<u64> = Vec::new();
191 let mut i: usize = 0;
192 while i < num_candidates
193 invariant
194 i <= num_candidates,
195 scores.len() == i,
196 forall|k: int| 0 <= k < i ==> scores@[k] == 0,
197 decreases num_candidates - i,
198 {
199 scores.push(0);
200 i = i + 1;
201 }
202 CompetitiveSelectionHard { scores, allocation: None }
203 }
204
205 pub fn evaluate(&mut self)
208 requires old(self).scores.len() >= 1,
209 ensures
210 final(self).scores@ == old(self).scores@,
211 final(self).allocation is Some,
212 final(self).inv(),
213 {
214 let n = self.scores.len();
215 let mut best: usize = 0;
216 let mut i: usize = 1;
217 while i < n
218 invariant
219 1 <= i <= n,
220 best < i,
221 n == self.scores.len(),
222 forall|c: int| 0 <= c < i ==> #[trigger] self.scores@[c] <= self.scores@[best as int],
223 forall|c: int| 0 <= c < i ==>
227 (#[trigger] self.scores@[c] == self.scores@[best as int] ==> best <= c),
228 decreases n - i,
229 {
230 if self.scores[i] > self.scores[best] {
231 best = i;
232 }
233 i = i + 1;
234 }
235 self.allocation = Some(best);
236 }
237
238 pub fn update_score(&mut self, c: usize, v: u64)
240 requires c < old(self).scores.len(),
241 ensures
242 final(self).scores@ == old(self).scores@.update(c as int, v),
243 final(self).allocation is None,
244 final(self).inv(),
245 {
246 self.scores.set(c, v);
247 self.allocation = None;
248 }
249}
250
251pub struct CompetitiveSelectionHardExclusive {
259 pub num_seats: usize,
261 pub num_candidates: usize,
263 pub max_score: u64,
265 pub allocation: Vec<Option<u64>>,
267 pub scores: Vec<Vec<u64>>,
269}
270
271impl CompetitiveSelectionHardExclusive {
272 pub open spec fn type_invariant(&self) -> bool {
274 &&& self.num_candidates >= 1
275 &&& self.allocation.len() == self.num_seats
276 &&& self.scores.len() == self.num_seats
277 &&& (forall|s: int|
278 0 <= s < self.num_seats ==> (#[trigger] self.scores@[s]).len() == self.num_candidates)
279 &&& (forall|s: int|
280 #![trigger self.allocation@[s]]
281 0 <= s < self.num_seats ==> (self.allocation@[s] matches Some(w) ==>
282 (w as int) < self.num_candidates as int))
283 &&& (forall|s: int, c: int|
284 0 <= s < self.num_seats && 0 <= c < self.num_candidates as int ==>
285 #[trigger] self.scores@[s]@[c] <= self.max_score)
286 }
287
288 pub open spec fn available(&self, s: int, c: int) -> bool {
290 &&& 0 <= s < self.num_seats
291 &&& 0 <= c < self.num_candidates as int
292 &&& forall|t: int|
293 0 <= t < self.num_seats && t != s ==>
294 #[trigger] self.allocation@[t] != Some(c as u64)
295 }
296
297 pub open spec fn mutual_exclusion(&self) -> bool {
299 forall|s: int, t: int|
300 #![trigger self.allocation@[s], self.allocation@[t]]
301 0 <= s < self.num_seats && 0 <= t < self.num_seats && s != t
302 && self.allocation@[s] is Some ==>
303 self.allocation@[s] != self.allocation@[t]
304 }
305
306 pub open spec fn winner_optimality(&self) -> bool {
308 forall|s: int|
309 #![trigger self.allocation@[s]]
310 0 <= s < self.num_seats ==> (self.allocation@[s] matches Some(w) ==>
311 forall|c: int| self.available(s, c) ==>
312 #[trigger] self.scores@[s]@[c] <= self.scores@[s]@[w as int])
313 }
314
315 pub open spec fn winner_tie_break(&self) -> bool {
317 forall|s: int|
318 #![trigger self.allocation@[s]]
319 0 <= s < self.num_seats ==> (self.allocation@[s] matches Some(w) ==>
320 forall|c: int| self.available(s, c) &&
321 #[trigger] self.scores@[s]@[c] == self.scores@[s]@[w as int]
322 ==> (w as int) <= c)
323 }
324
325 pub open spec fn inv(&self) -> bool {
327 self.type_invariant() && self.mutual_exclusion()
328 && self.winner_optimality() && self.winner_tie_break()
329 }
330
331 pub fn new(
333 num_seats: usize,
334 num_candidates: usize,
335 max_score: u64,
336 ) -> (r: CompetitiveSelectionHardExclusive)
337 requires num_candidates >= 1,
338 ensures
339 r.num_seats == num_seats,
340 r.num_candidates == num_candidates,
341 r.max_score == max_score,
342 r.allocation.len() == num_seats,
343 r.scores.len() == num_seats,
344 forall|s: int| 0 <= s < num_seats ==> r.allocation@[s] is None,
345 forall|s: int| 0 <= s < num_seats ==>
346 (#[trigger] r.scores@[s]).len() == num_candidates,
347 forall|s: int, c: int|
348 0 <= s < num_seats && 0 <= c < num_candidates ==>
349 #[trigger] r.scores@[s]@[c] == 0,
350 r.inv(),
351 {
352 let mut allocation: Vec<Option<u64>> = Vec::new();
353 let mut scores: Vec<Vec<u64>> = Vec::new();
354 let mut s: usize = 0;
355 while s < num_seats
356 invariant
357 s <= num_seats,
358 allocation.len() == s,
359 scores.len() == s,
360 forall|i: int| 0 <= i < s ==> allocation@[i] is None,
361 forall|i: int| 0 <= i < s ==>
362 (#[trigger] scores@[i]).len() == num_candidates,
363 forall|i: int, j: int|
364 0 <= i < s && 0 <= j < num_candidates ==>
365 #[trigger] scores@[i]@[j] == 0,
366 decreases num_seats - s,
367 {
368 let mut row: Vec<u64> = Vec::new();
369 let mut c: usize = 0;
370 while c < num_candidates
371 invariant
372 c <= num_candidates,
373 row.len() == c,
374 forall|j: int| 0 <= j < c ==> #[trigger] row@[j] == 0,
375 decreases num_candidates - c,
376 {
377 row.push(0);
378 c = c + 1;
379 }
380 allocation.push(None);
381 scores.push(row);
382 s = s + 1;
383 }
384 CompetitiveSelectionHardExclusive {
385 num_seats,
386 num_candidates,
387 max_score,
388 allocation,
389 scores,
390 }
391 }
392
393 pub fn score_at(&self, s: usize, c: usize) -> (v: u64)
395 requires
396 s < self.scores.len(),
397 c < self.scores@[s as int].len(),
398 ensures v == self.scores@[s as int]@[c as int],
399 {
400 self.scores[s][c]
401 }
402
403 pub fn candidate_available(&self, s: usize, c: usize) -> (available: bool)
405 requires
406 self.type_invariant(),
407 s < self.num_seats,
408 c < self.num_candidates,
409 ensures available == self.available(s as int, c as int),
410 {
411 let mut t: usize = 0;
412 while t < self.num_seats
413 invariant
414 t <= self.num_seats,
415 s < self.num_seats,
416 c < self.num_candidates,
417 self.type_invariant(),
418 forall|j: int| 0 <= j < t && j != s as int ==>
419 #[trigger] self.allocation@[j] != Some(c as u64),
420 decreases self.num_seats - t,
421 {
422 if t != s && self.allocation[t] == Some(c as u64) {
423 return false;
424 }
425 t = t + 1;
426 }
427 true
428 }
429
430 pub fn has_available(&self, s: usize) -> (available: bool)
432 requires
433 self.type_invariant(),
434 s < self.num_seats,
435 ensures available == (exists|c: int| self.available(s as int, c)),
436 {
437 let mut c: usize = 0;
438 while c < self.num_candidates
439 invariant
440 c <= self.num_candidates,
441 s < self.num_seats,
442 self.type_invariant(),
443 forall|j: int| 0 <= j < c ==>
444 !self.available(s as int, j),
445 decreases self.num_candidates - c,
446 {
447 if self.candidate_available(s, c) {
448 return true;
449 }
450 c = c + 1;
451 }
452 false
453 }
454
455 pub fn evaluate(&mut self, s: usize)
458 requires
459 old(self).inv(),
460 s < old(self).num_seats,
461 old(self).allocation@[s as int] is None,
462 exists|c: int| old(self).available(s as int, c),
463 ensures
464 final(self).num_seats == old(self).num_seats,
465 final(self).num_candidates == old(self).num_candidates,
466 final(self).max_score == old(self).max_score,
467 final(self).scores@ == old(self).scores@,
468 final(self).allocation.len() == old(self).allocation.len(),
469 forall|t: int| 0 <= t < final(self).num_seats && t != s as int ==>
470 final(self).allocation@[t] == old(self).allocation@[t],
471 final(self).allocation@[s as int] is Some,
472 final(self).inv(),
473 {
474 let n = self.num_candidates;
475 let mut best: usize = 0;
476 let mut found: bool = false;
477 let mut i: usize = 0;
478 while i < n
479 invariant
480 i <= n,
481 n == self.num_candidates,
482 s < self.num_seats,
483 self.inv(),
484 self.scores@ == old(self).scores@,
485 self.allocation@ == old(self).allocation@,
486 self.num_seats == old(self).num_seats,
487 self.num_candidates == old(self).num_candidates,
488 self.max_score == old(self).max_score,
489 found == (exists|c: int| 0 <= c < i && self.available(s as int, c)),
490 found ==> best < i,
491 found ==> self.available(s as int, best as int),
492 found ==> forall|c: int| 0 <= c < i && self.available(s as int, c) ==>
493 #[trigger] self.scores@[s as int]@[c]
494 <= self.scores@[s as int]@[best as int],
495 found ==> forall|c: int| 0 <= c < i && self.available(s as int, c)
496 && #[trigger] self.scores@[s as int]@[c]
497 == self.scores@[s as int]@[best as int] ==> best as int <= c,
498 decreases n - i,
499 {
500 if self.candidate_available(s, i) {
501 if !found {
502 best = i;
503 found = true;
504 } else {
505 let vi = self.score_at(s, i);
506 let vb = self.score_at(s, best);
507 if vi > vb {
508 best = i;
509 }
510 }
511 }
512 i = i + 1;
513 }
514 assert(found);
515 assert(self.available(s as int, best as int));
516 assert(forall|c: int| self.available(s as int, c) ==>
517 #[trigger] self.scores@[s as int]@[c]
518 <= self.scores@[s as int]@[best as int]);
519 assert(forall|c: int| self.available(s as int, c)
520 && #[trigger] self.scores@[s as int]@[c]
521 == self.scores@[s as int]@[best as int] ==> best as int <= c);
522 assert((best as u64) as int == best as int);
523 self.allocation.set(s, Some(best as u64));
524
525 assert(self.type_invariant()) by {
526 assert forall|t: int|
527 #![trigger self.allocation@[t]]
528 0 <= t < self.num_seats implies (self.allocation@[t] matches Some(w) ==>
529 (w as int) < self.num_candidates as int) by {
530 if t != s as int {
531 assert(self.allocation@[t] == old(self).allocation@[t]);
532 }
533 }
534 }
535 assert(self.mutual_exclusion()) by {
536 assert forall|a: int, b: int|
537 #![trigger self.allocation@[a], self.allocation@[b]]
538 0 <= a < self.num_seats && 0 <= b < self.num_seats && a != b
539 && self.allocation@[a] is Some implies
540 self.allocation@[a] != self.allocation@[b] by {
541 if a == s as int {
542 assert(self.allocation@[a] == Some(best as u64));
543 assert(old(self).available(s as int, best as int));
544 assert(self.allocation@[b] == old(self).allocation@[b]);
545 } else if b == s as int {
546 assert(self.allocation@[b] == Some(best as u64));
547 assert(old(self).available(s as int, best as int));
548 assert(self.allocation@[a] == old(self).allocation@[a]);
549 } else {
550 assert(self.allocation@[a] == old(self).allocation@[a]);
551 assert(self.allocation@[b] == old(self).allocation@[b]);
552 }
553 }
554 }
555 assert(self.winner_optimality()) by {
556 assert forall|t: int|
557 #![trigger self.allocation@[t]]
558 0 <= t < self.num_seats implies (self.allocation@[t] matches Some(w) ==>
559 forall|c: int| self.available(t, c) ==>
560 #[trigger] self.scores@[t]@[c] <= self.scores@[t]@[w as int]) by {
561 if t == s as int {
562 assert(self.allocation@[t] == Some(best as u64));
563 assert forall|c: int| self.available(t, c) implies
564 old(self).available(t, c) by {
565 assert forall|r: int|
566 0 <= r < self.num_seats && r != t implies
567 old(self).allocation@[r] != Some(c as u64) by {
568 assert(r != s as int);
569 assert(self.allocation@[r] == old(self).allocation@[r]);
570 }
571 }
572 } else {
573 assert(self.allocation@[t] == old(self).allocation@[t]);
574 assert forall|c: int| self.available(t, c) implies
575 old(self).available(t, c) by {
576 assert forall|r: int|
577 0 <= r < self.num_seats && r != t implies
578 old(self).allocation@[r] != Some(c as u64) by {
579 if r == s as int {
580 assert(old(self).allocation@[r] is None);
581 } else {
582 assert(self.allocation@[r] == old(self).allocation@[r]);
583 }
584 }
585 }
586 }
587 }
588 }
589 assert(self.winner_tie_break()) by {
590 assert forall|t: int|
591 #![trigger self.allocation@[t]]
592 0 <= t < self.num_seats implies (self.allocation@[t] matches Some(w) ==>
593 forall|c: int| self.available(t, c)
594 && #[trigger] self.scores@[t]@[c] == self.scores@[t]@[w as int]
595 ==> (w as int) <= c) by {
596 if t == s as int {
597 assert(self.allocation@[t] == Some(best as u64));
598 assert forall|c: int| self.available(t, c) implies
599 old(self).available(t, c) by {
600 assert forall|r: int|
601 0 <= r < self.num_seats && r != t implies
602 old(self).allocation@[r] != Some(c as u64) by {
603 assert(r != s as int);
604 assert(self.allocation@[r] == old(self).allocation@[r]);
605 }
606 }
607 } else {
608 assert(self.allocation@[t] == old(self).allocation@[t]);
609 assert forall|c: int| self.available(t, c) implies
610 old(self).available(t, c) by {
611 assert forall|r: int|
612 0 <= r < self.num_seats && r != t implies
613 old(self).allocation@[r] != Some(c as u64) by {
614 if r == s as int {
615 assert(old(self).allocation@[r] is None);
616 } else {
617 assert(self.allocation@[r] == old(self).allocation@[r]);
618 }
619 }
620 }
621 }
622 }
623 }
624 }
625
626 pub fn update_score(&mut self, s: usize, c: usize, v: u64)
629 requires
630 old(self).inv(),
631 s < old(self).num_seats,
632 c < old(self).num_candidates,
633 v <= old(self).max_score,
634 ensures
635 final(self).num_seats == old(self).num_seats,
636 final(self).num_candidates == old(self).num_candidates,
637 final(self).max_score == old(self).max_score,
638 final(self).scores.len() == old(self).scores.len(),
639 forall|t: int| 0 <= t < final(self).num_seats && t != s as int ==>
640 final(self).scores@[t]@ == old(self).scores@[t]@,
641 final(self).scores@[s as int]@
642 == old(self).scores@[s as int]@.update(c as int, v),
643 forall|t: int| 0 <= t < final(self).num_seats ==>
644 final(self).allocation@[t] is None,
645 final(self).inv(),
646 {
647 let ghost old_row = self.scores@[s as int]@;
648 let row_len = self.scores[s].len();
649 let mut new_row: Vec<u64> = Vec::new();
650 let mut k: usize = 0;
651 while k < row_len
652 invariant
653 k <= row_len,
654 row_len == self.scores@[s as int].len(),
655 row_len == self.num_candidates,
656 c < row_len,
657 s < self.num_seats,
658 self.scores.len() == self.num_seats,
659 new_row.len() == k,
660 old_row == old(self).scores@[s as int]@,
661 old_row.len() == row_len,
662 forall|j: int| 0 <= j < k ==>
663 new_row@[j] == (if j == c as int { v } else { old_row[j] }),
664 self.scores@ == old(self).scores@,
665 self.allocation@ == old(self).allocation@,
666 self.num_seats == old(self).num_seats,
667 self.num_candidates == old(self).num_candidates,
668 self.max_score == old(self).max_score,
669 decreases row_len - k,
670 {
671 if k == c {
672 new_row.push(v);
673 } else {
674 let x = self.score_at(s, k);
675 new_row.push(x);
676 }
677 k = k + 1;
678 }
679 assert(new_row@ == old_row.update(c as int, v)) by {
680 assert(new_row.len() == old_row.len());
681 assert forall|j: int| 0 <= j < new_row.len() implies
682 new_row@[j] == old_row.update(c as int, v)[j] by {
683 if j == c as int {
684 } else {
685 }
686 }
687 }
688 self.scores.set(s, new_row);
689 let ghost updated_scores = self.scores@;
690 assert(updated_scores.len() == old(self).scores@.len());
691 assert(updated_scores[s as int]@ == old(self).scores@[s as int]@.update(c as int, v));
692 assert forall|j: int| 0 <= j < self.num_seats && j != s as int implies
693 updated_scores[j]@ == old(self).scores@[j]@ by {
694 }
695
696 let mut t: usize = 0;
697 while t < self.num_seats
698 invariant
699 t <= self.num_seats,
700 self.num_seats == old(self).num_seats,
701 self.num_candidates == old(self).num_candidates,
702 self.max_score == old(self).max_score,
703 self.allocation.len() == self.num_seats,
704 forall|j: int| 0 <= j < t ==> self.allocation@[j] is None,
705 forall|j: int| t <= j < self.num_seats ==>
706 self.allocation@[j] == old(self).allocation@[j],
707 self.scores@ == updated_scores,
708 updated_scores.len() == old(self).scores@.len(),
709 updated_scores[s as int]@
710 == old(self).scores@[s as int]@.update(c as int, v),
711 forall|j: int| 0 <= j < self.num_seats && j != s as int ==>
712 updated_scores[j]@ == old(self).scores@[j]@,
713 decreases self.num_seats - t,
714 {
715 self.allocation.set(t, None);
716 t = t + 1;
717 }
718
719 assert(self.type_invariant()) by {
720 assert forall|i: int| 0 <= i < self.num_seats implies
721 (#[trigger] self.scores@[i]).len() == self.num_candidates by {
722 if i != s as int {
723 assert(self.scores@[i]@ == old(self).scores@[i]@);
724 }
725 }
726 assert forall|i: int, j: int|
727 0 <= i < self.num_seats && 0 <= j < self.num_candidates as int implies
728 #[trigger] self.scores@[i]@[j] <= self.max_score by {
729 if i == s as int && j == c as int {
730 } else if i == s as int {
731 } else {
732 assert(self.scores@[i]@ == old(self).scores@[i]@);
733 }
734 }
735 }
736 }
737}
738
739pub open spec fn priority_ge(scores: Seq<u64>, extra: Seq<u64>, a: int, b: int) -> bool {
749 scores[a] as int * (2 * extra[b] as int + 1) >= scores[b] as int * (2 * extra[a] as int + 1)
750}
751
752pub open spec fn priority_gt(scores: Seq<u64>, extra: Seq<u64>, a: int, b: int) -> bool {
755 scores[a] as int * (2 * extra[b] as int + 1) > scores[b] as int * (2 * extra[a] as int + 1)
756}
757
758pub open spec fn priority_equal(scores: Seq<u64>, extra: Seq<u64>, a: int, b: int) -> bool {
760 scores[a] as int * (2 * extra[b] as int + 1) == scores[b] as int * (2 * extra[a] as int + 1)
761}
762
763pub proof fn lemma_priority_gt_ge_not_equal(
767 scores: Seq<u64>, extra: Seq<u64>, a: int, b: int, c: int,
768)
769 requires
770 priority_gt(scores, extra, a, b),
771 priority_ge(scores, extra, b, c),
772 ensures
773 !priority_equal(scores, extra, a, c),
774{
775 let sa = scores[a] as int;
776 let sb = scores[b] as int;
777 let sc = scores[c] as int;
778 let a_den = 2 * extra[a] as int + 1;
779 let b_den = 2 * extra[b] as int + 1;
780 let c_den = 2 * extra[c] as int + 1;
781 assert(sa * b_den * c_den > sb * a_den * c_den) by (nonlinear_arith)
782 requires sa * b_den > sb * a_den, c_den > 0;
783 assert(sb * c_den * a_den >= sc * b_den * a_den) by (nonlinear_arith)
784 requires sb * c_den >= sc * b_den, a_den > 0;
785 assert(sa * b_den * c_den > sc * b_den * a_den) by (nonlinear_arith)
786 requires
787 sa * b_den * c_den > sb * a_den * c_den,
788 sb * c_den * a_den >= sc * b_den * a_den;
789 assert(sa * c_den > sc * a_den) by (nonlinear_arith)
790 requires sa * b_den * c_den > sc * b_den * a_den, b_den > 0;
791}
792
793pub open spec fn priority_winner(scores: Seq<u64>, extra: Seq<u64>, w: int) -> bool {
795 0 <= w < scores.len()
796 && scores.len() == extra.len()
797 && (forall|c: int| 0 <= c < scores.len() ==> priority_ge(scores, extra, w, c))
798 && (forall|c: int| 0 <= c < scores.len() && priority_equal(scores, extra, w, c)
799 ==> w <= c)
800}
801
802pub open spec fn award_trace(scores: Seq<u64>, extra: Seq<u64>, steps: nat) -> bool
804 decreases steps,
805{
806 if steps == 0 {
807 scores.len() == extra.len()
808 && forall|index: int| 0 <= index < extra.len() ==>
809 #[trigger] extra[index] == 0
810 } else {
811 exists|before: Seq<u64>, winner: int| {
812 &&& award_trace(scores, before, (steps - 1) as nat)
813 &&& priority_winner(scores, before, winner)
814 &&& extra == before.update(winner, (before[winner] + 1) as u64)
815 }
816 }
817}
818
819pub proof fn lemma_priority_strict(scores: Seq<u64>, extra: Seq<u64>, a: int, b: int)
824 requires
825 priority_ge(scores, extra, a, b),
826 scores[a] < scores[b],
827 ensures
828 extra[a] < extra[b],
829{
830 if extra[a] >= extra[b] {
831 assert(scores[b] as int * (2 * extra[a] as int + 1)
832 > scores[a] as int * (2 * extra[b] as int + 1)) by (nonlinear_arith)
833 requires scores[a] < scores[b], extra[a] >= extra[b], scores[a] >= 0, scores[b] >= 0,
834 extra[a] >= 0, extra[b] >= 0;
835 assert(false);
836 }
837}
838
839pub proof fn lemma_priority_tie(scores: Seq<u64>, extra: Seq<u64>, a: int, b: int)
842 requires
843 priority_ge(scores, extra, a, b),
844 scores[a] == scores[b],
845 scores[a] > 0,
846 ensures
847 extra[a] <= extra[b],
848{
849 if extra[a] > extra[b] {
850 assert(scores[b] as int * (2 * extra[a] as int + 1)
851 > scores[a] as int * (2 * extra[b] as int + 1)) by (nonlinear_arith)
852 requires scores[a] == scores[b], scores[a] > 0, extra[a] > extra[b],
853 extra[a] >= 0, extra[b] >= 0;
854 assert(false);
855 }
856}
857
858pub proof fn lemma_priority_ge_trans(scores: Seq<u64>, extra: Seq<u64>, a: int, b: int, c: int)
867 requires
868 priority_ge(scores, extra, a, b),
869 priority_ge(scores, extra, b, c),
870 ensures
871 priority_ge(scores, extra, a, c),
872{
873 let sa = scores[a] as int;
874 let sb = scores[b] as int;
875 let sc = scores[c] as int;
876 let a_den = 2 * extra[a] as int + 1;
877 let b_den = 2 * extra[b] as int + 1;
878 let c_den = 2 * extra[c] as int + 1;
879 assert(sa * b_den * c_den >= sb * a_den * c_den) by (nonlinear_arith)
880 requires sa * b_den >= sb * a_den, c_den >= 0;
881 assert(sb * c_den * a_den >= sc * b_den * a_den) by (nonlinear_arith)
882 requires sb * c_den >= sc * b_den, a_den >= 0;
883 assert(sa * b_den * c_den >= sc * b_den * a_den) by (nonlinear_arith)
884 requires
885 sa * b_den * c_den >= sb * a_den * c_den,
886 sb * c_den * a_den >= sc * b_den * a_den;
887 assert(sa * c_den >= sc * a_den) by (nonlinear_arith)
888 requires
889 sa * b_den * c_den >= sc * b_den * a_den,
890 b_den > 0;
891}
892
893pub proof fn lemma_priority_lhs_monotone(s: int, e: int, f: int)
897 requires s >= 0, e <= f,
898 ensures s * (2 * e + 1) <= s * (2 * f + 1),
899{
900 assert(s * (2 * e + 1) <= s * (2 * f + 1)) by (nonlinear_arith)
901 requires s >= 0, e <= f;
902}
903
904pub struct CompetitiveSelectionSoft {
907 pub extra: Vec<u64>,
909 pub scores: Vec<u64>,
911 pub weight_total: u64,
913 pub max_score: u64,
915}
916
917impl CompetitiveSelectionSoft {
918 pub open spec fn well_formed(&self) -> bool {
920 self.extra.len() == self.scores.len() && self.extra.len() > 0
921 }
922
923 pub open spec fn score_bounds(&self) -> bool {
925 forall|i: int| 0 <= i < self.scores.len() ==>
926 #[trigger] self.scores@[i] >= 1 && self.scores@[i] <= self.max_score
927 }
928
929 pub open spec fn weight(&self, i: int) -> int {
932 1 + self.extra@[i] as int
933 }
934
935 pub open spec fn universal_contribution(&self) -> bool {
937 forall|i: int| 0 <= i < self.extra.len() ==> #[trigger] self.weight(i) > 0
938 }
939
940 pub open spec fn score_order_preservation(&self) -> bool {
943 forall|i: int, j: int|
944 0 <= i < self.scores.len() && 0 <= j < self.scores.len()
945 && self.scores@[i] < self.scores@[j]
946 ==> #[trigger] self.weight(i) <= #[trigger] self.weight(j)
947 }
948
949 pub open spec fn tie_boundedness(&self) -> bool {
951 forall|i: int, j: int|
952 0 <= i < self.scores.len() && 0 <= j < self.scores.len()
953 && self.scores@[i] == self.scores@[j]
954 ==> #[trigger] self.weight(i) <= #[trigger] self.weight(j) + 1
955 }
956
957 pub open spec fn normalization(&self) -> bool {
959 sum_to(self.extra@, self.extra@.len() as int) + self.extra@.len() as int
960 == self.weight_total as int
961 }
962
963 pub open spec fn bounded_total(&self) -> bool {
965 sum_to(self.extra@, self.extra@.len() as int) + self.extra@.len() as int
966 <= self.weight_total as int
967 }
968
969 pub open spec fn terminal(&self) -> bool {
971 sum_to(self.extra@, self.extra@.len() as int)
972 == self.weight_total as int - self.extra@.len() as int
973 }
974
975 pub open spec fn normalization_when_terminal(&self) -> bool {
977 self.terminal() ==> self.normalization()
978 }
979
980 pub open spec fn bounded(&self) -> bool {
984 self.weight_total <= 1_000_000_000
985 && self.max_score <= 1_000_000_000
986 && forall|k: int| 0 <= k < self.extra.len() ==> #[trigger] self.extra@[k] <= self.weight_total
987 }
988
989 pub open spec fn mutable_score_inv(&self) -> bool {
992 self.well_formed()
993 && self.weight_total as int >= self.extra.len() as int
994 && self.score_bounds()
995 && self.bounded()
996 && self.bounded_total()
997 && self.normalization_when_terminal()
998 && self.universal_contribution()
999 && self.score_order_preservation()
1000 && self.tie_boundedness()
1001 }
1002
1003 pub open spec fn webster_allocation(&self) -> bool {
1010 forall|i: int, j: int| #![trigger self.extra@[i], self.extra@[j]]
1011 0 <= i < self.extra.len() && 0 <= j < self.extra.len()
1012 && self.extra@[i] >= 1
1013 ==> self.scores@[i] as int * (2 * self.extra@[j] as int + 1)
1014 >= self.scores@[j] as int * (2 * self.extra@[i] as int - 1)
1015 }
1016
1017 pub open spec fn inv(&self) -> bool {
1019 self.mutable_score_inv() && self.normalization()
1020 }
1021
1022 pub fn weight_at(&self, i: usize) -> (w: u64)
1024 requires i < self.extra.len(), self.bounded(),
1025 ensures w as int == self.weight(i as int),
1026 {
1027 self.extra[i] + 1
1028 }
1029
1030 pub fn assigned_weight(&self) -> (total: u64)
1032 requires self.mutable_score_inv(),
1033 ensures
1034 total as int
1035 == sum_to(self.extra@, self.extra@.len() as int) + self.extra@.len() as int,
1036 total <= self.weight_total,
1037 {
1038 let len = self.extra.len();
1039 let mut total = len as u64;
1040 let mut index: usize = 0;
1041 proof {
1042 assert(self.bounded_total());
1043 }
1044 while index < len
1045 invariant
1046 index <= len,
1047 len == self.extra.len(),
1048 total as int == sum_to(self.extra@, index as int) + len as int,
1049 total <= self.weight_total,
1050 sum_to(self.extra@, len as int) + len as int <= self.weight_total as int,
1051 decreases len - index,
1052 {
1053 proof {
1054 assert(sum_to(self.extra@, index as int + 1)
1055 == self.extra@[index as int] as int
1056 + sum_to(self.extra@, index as int));
1057 lemma_sum_prefix_le(self.extra@, index as int + 1, len as int);
1058 assert(sum_to(self.extra@, index as int + 1) + len as int
1059 <= self.weight_total as int);
1060 assert(total as int + self.extra@[index as int] as int
1061 <= self.weight_total as int);
1062 }
1063 total = total + self.extra[index];
1064 index += 1;
1065 }
1066 total
1067 }
1068
1069 pub fn init(scores: Vec<u64>, weight_total: u64, max_score: u64) -> (s: CompetitiveSelectionSoft)
1073 requires
1074 scores.len() >= 1,
1075 weight_total >= scores.len() as u64,
1076 weight_total <= 1_000_000_000,
1077 max_score <= 1_000_000_000,
1078 forall|i: int| 0 <= i < scores.len() ==>
1079 #[trigger] scores@[i] >= 1 && scores@[i] <= max_score,
1080 ensures
1081 s.scores@ == scores@,
1082 s.weight_total == weight_total,
1083 s.max_score == max_score,
1084 s.extra@.len() == scores@.len(),
1085 forall|i: int| 0 <= i < s.extra.len() ==> s.extra@[i] == 0,
1086 award_trace(scores@, s.extra@, 0),
1087 s.mutable_score_inv(),
1088 {
1089 let n = scores.len();
1090 let mut extra: Vec<u64> = Vec::new();
1091 let mut i: usize = 0;
1092 while i < n
1093 invariant
1094 i <= n,
1095 extra.len() == i,
1096 forall|k: int| 0 <= k < i ==> extra@[k] == 0,
1097 sum_to(extra@, i as int) == 0,
1098 decreases n - i,
1099 {
1100 let ghost eold = extra@;
1101 extra.push(0);
1102 proof {
1103 lemma_sum_push_prefix(eold, 0, i as int);
1104 assert(sum_to(extra@, i as int + 1)
1105 == extra@[i as int] as int + sum_to(extra@, i as int));
1106 }
1107 i = i + 1;
1108 }
1109 proof {
1110 assert forall|a: int, b: int| 0 <= a < n && 0 <= b < n
1111 && scores@[a] < scores@[b]
1112 implies 1 + extra@[a] as int <= 1 + extra@[b] as int by {}
1113 assert forall|a: int, b: int| 0 <= a < n && 0 <= b < n
1114 && scores@[a] == scores@[b]
1115 implies 1 + extra@[a] as int <= 1 + extra@[b] as int + 1 by {}
1116 }
1117 CompetitiveSelectionSoft { extra, scores, weight_total, max_score }
1118 }
1119
1120 pub fn new(scores: Vec<u64>, weight_total: u64, max_score: u64) -> (s: CompetitiveSelectionSoft)
1125 requires
1126 scores.len() >= 1,
1127 weight_total >= scores.len() as u64,
1128 weight_total <= 1_000_000_000,
1129 max_score <= 1_000_000_000,
1130 forall|i: int| 0 <= i < scores.len() ==> #[trigger] scores@[i] >= 1 && scores@[i] <= max_score,
1131 ensures
1132 s.scores@ == scores@,
1133 s.weight_total == weight_total,
1134 s.max_score == max_score,
1135 s.mutable_score_inv(),
1136 s.inv(),
1137 s.webster_allocation(),
1138 award_trace(
1139 scores@,
1140 s.extra@,
1141 (weight_total - scores@.len() as u64) as nat,
1142 ),
1143 {
1144 let n = scores.len();
1145 let pool: u64 = weight_total - (n as u64);
1146 let mut extra: Vec<u64> = Vec::new();
1147 let mut i: usize = 0;
1148 while i < n
1149 invariant
1150 i <= n,
1151 extra.len() == i,
1152 forall|k: int| 0 <= k < i ==> extra@[k] == 0,
1153 sum_to(extra@, i as int) == 0,
1154 decreases n - i,
1155 {
1156 let ghost eold = extra@;
1157 extra.push(0);
1158 proof {
1159 lemma_sum_push_prefix(eold, 0, i as int);
1160 assert(sum_to(extra@, i as int + 1) == extra@[i as int] as int + sum_to(extra@, i as int));
1161 }
1162 i = i + 1;
1163 }
1164
1165 assert(award_trace(scores@, extra@, 0));
1166 let mut awarded: u64 = 0;
1167 while awarded < pool
1168 invariant
1169 extra.len() == n,
1170 n == scores.len(),
1171 n >= 1,
1172 weight_total >= n as u64,
1173 weight_total <= 1_000_000_000,
1174 pool == weight_total - (n as u64),
1175 awarded <= pool,
1176 forall|k: int| 0 <= k < n ==> #[trigger] scores@[k] >= 1 && scores@[k] <= 1_000_000_000,
1177 sum_to(extra@, n as int) == awarded as int,
1178 forall|k: int| 0 <= k < n ==> #[trigger] extra@[k] <= awarded,
1179 forall|c: int, d: int|
1180 0 <= c < n && 0 <= d < n && scores@[c] < scores@[d]
1181 ==> 1 + extra@[c] as int <= 1 + extra@[d] as int,
1182 forall|c: int, d: int|
1183 0 <= c < n && 0 <= d < n && scores@[c] == scores@[d]
1184 ==> 1 + extra@[c] as int <= 1 + extra@[d] as int + 1,
1185 forall|c: int, d: int|
1191 0 <= c < n && 0 <= d < n && extra@[c] >= 1
1192 ==> scores@[c] as int * (2 * extra@[d] as int + 1)
1193 >= scores@[d] as int * (2 * extra@[c] as int - 1),
1194 award_trace(scores@, extra@, awarded as nat),
1195 decreases pool - awarded,
1196 {
1197 let mut best: usize = 0;
1198 let mut j: usize = 1;
1199 while j < n
1200 invariant
1201 1 <= j <= n,
1202 best < j,
1203 extra.len() == n,
1204 n == scores.len(),
1205 weight_total <= 1_000_000_000,
1206 pool == weight_total - (n as u64),
1207 awarded <= pool,
1208 forall|k: int| 0 <= k < n ==> #[trigger] scores@[k] <= 1_000_000_000,
1209 forall|k: int| 0 <= k < n ==> #[trigger] extra@[k] <= awarded,
1210 forall|c: int| 0 <= c < j ==> priority_ge(scores@, extra@, best as int, c),
1211 forall|c: int| 0 <= c < j
1212 && priority_equal(scores@, extra@, best as int, c)
1213 ==> best as int <= c,
1214 decreases n - j,
1215 {
1216 proof {
1217 assert((scores@[j as int] as int) * (2 * (extra@[best as int] as int) + 1)
1218 <= 1_000_000_000 * (2 * 1_000_000_000 + 1)) by (nonlinear_arith)
1219 requires
1220 scores@[j as int] as int <= 1_000_000_000,
1221 extra@[best as int] as int <= 1_000_000_000;
1222 assert((scores@[best as int] as int) * (2 * (extra@[j as int] as int) + 1)
1223 <= 1_000_000_000 * (2 * 1_000_000_000 + 1)) by (nonlinear_arith)
1224 requires
1225 scores@[best as int] as int <= 1_000_000_000,
1226 extra@[j as int] as int <= 1_000_000_000;
1227 }
1228 let lhs: u128 = (scores[j] as u128) * (2 * (extra[best] as u128) + 1);
1229 let rhs: u128 = (scores[best] as u128) * (2 * (extra[j] as u128) + 1);
1230 proof {
1231 assert(lhs as int == scores@[j as int] as int * (2 * extra@[best as int] as int + 1));
1232 assert(rhs as int == scores@[best as int] as int * (2 * extra@[j as int] as int + 1));
1233 }
1234 let old_best: usize = best;
1235 let old_j: usize = j;
1236 let _ = (old_best, old_j);
1237 if lhs > rhs {
1238 best = j;
1239 proof {
1240 assert(priority_ge(scores@, extra@, old_j as int, old_best as int));
1241 assert(priority_gt(scores@, extra@, old_j as int, old_best as int));
1242 assert forall|c: int| 0 <= c < old_j as int + 1
1243 implies priority_ge(scores@, extra@, best as int, c)
1244 by {
1245 if c == old_best as int {
1246 assert(priority_ge(scores@, extra@, old_j as int, old_best as int));
1247 } else if c < old_j as int {
1248 lemma_priority_ge_trans(scores@, extra@, old_j as int, old_best as int, c);
1249 } else {
1250 }
1252 }
1253 assert forall|c: int| 0 <= c < old_j as int + 1
1254 && priority_equal(scores@, extra@, best as int, c)
1255 implies best as int <= c
1256 by {
1257 if c < old_j as int {
1258 lemma_priority_gt_ge_not_equal(
1259 scores@,
1260 extra@,
1261 old_j as int,
1262 old_best as int,
1263 c,
1264 );
1265 assert(false);
1266 }
1267 }
1268 }
1269 } else {
1270 proof {
1271 assert(priority_ge(scores@, extra@, old_best as int, old_j as int));
1272 assert forall|c: int| 0 <= c < old_j as int + 1
1273 implies priority_ge(scores@, extra@, best as int, c)
1274 by {
1275 if c == old_j as int {
1276 assert(priority_ge(scores@, extra@, old_best as int, old_j as int));
1277 } else {
1278 }
1280 }
1281 assert forall|c: int| 0 <= c < old_j as int + 1
1282 && priority_equal(scores@, extra@, best as int, c)
1283 implies best as int <= c
1284 by {
1285 if c == old_j as int {
1286 assert(best < old_j);
1287 }
1288 }
1289 }
1290 }
1291 j = j + 1;
1292 }
1293 let ghost extra_old: Seq<u64> = extra@;
1294 let ghost best_g: int = best as int;
1295 proof {
1300 assert forall|c: int, d: int| 0 <= c < n && 0 <= d < n && scores@[c] < scores@[d]
1301 implies 1 + extra_old[c] as int <= 1 + extra_old[d] as int
1302 by {}
1303 assert forall|c: int, d: int| 0 <= c < n && 0 <= d < n && scores@[c] == scores@[d]
1304 implies 1 + extra_old[c] as int <= 1 + extra_old[d] as int + 1
1305 by {}
1306 assert forall|c: int, d: int| 0 <= c < n && 0 <= d < n && extra_old[c] >= 1
1307 implies scores@[c] as int * (2 * extra_old[d] as int + 1)
1308 >= scores@[d] as int * (2 * extra_old[c] as int - 1)
1309 by {}
1310 assert forall|c: int| 0 <= c < n
1311 implies priority_ge(scores@, extra_old, best_g, c)
1312 by {}
1313 assert(priority_winner(scores@, extra_old, best_g));
1314 }
1315 extra.set(best, extra[best] + 1);
1316 proof {
1317 assert(award_trace(scores@, extra@, awarded as nat + 1)) by {
1318 assert(exists|before: Seq<u64>, winner: int| {
1319 &&& award_trace(scores@, before, awarded as nat)
1320 &&& priority_winner(scores@, before, winner)
1321 &&& extra@ == before.update(
1322 winner,
1323 (before[winner] + 1) as u64,
1324 )
1325 }) by {
1326 assert(award_trace(scores@, extra_old, awarded as nat));
1327 assert(priority_winner(scores@, extra_old, best_g));
1328 assert(extra@ == extra_old.update(
1329 best_g,
1330 (extra_old[best_g] + 1) as u64,
1331 ));
1332 }
1333 }
1334 assert forall|c: int, d: int| 0 <= c < n && 0 <= d < n && scores@[c] < scores@[d]
1335 implies 1 + extra@[c] as int <= 1 + extra@[d] as int
1336 by {
1337 if c == best_g && d == best_g {
1338 assert(false);
1339 } else if c == best_g {
1340 lemma_priority_strict(scores@, extra_old, best_g, d);
1341 } else if d == best_g {
1342 assert(1 + extra_old[c] as int <= 1 + extra_old[best_g] as int);
1343 } else {
1344 assert(1 + extra_old[c] as int <= 1 + extra_old[d] as int);
1345 }
1346 }
1347 assert forall|c: int, d: int| 0 <= c < n && 0 <= d < n && scores@[c] == scores@[d]
1348 implies 1 + extra@[c] as int <= 1 + extra@[d] as int + 1
1349 by {
1350 if c == best_g && d == best_g {
1351 assert(1 + extra_old[c] as int <= 1 + extra_old[d] as int + 1);
1352 } else if c == best_g {
1353 if scores@[best_g] > 0 {
1354 lemma_priority_tie(scores@, extra_old, best_g, d);
1355 } else {
1356 assert(1 + extra_old[c] as int <= 1 + extra_old[d] as int + 1);
1357 }
1358 } else if d == best_g {
1359 assert(1 + extra_old[c] as int <= 1 + extra_old[d] as int + 1);
1360 } else {
1361 assert(1 + extra_old[c] as int <= 1 + extra_old[d] as int + 1);
1362 }
1363 }
1364 assert forall|c: int, d: int| 0 <= c < n && 0 <= d < n && extra@[c] >= 1
1366 implies scores@[c] as int * (2 * extra@[d] as int + 1)
1367 >= scores@[d] as int * (2 * extra@[c] as int - 1)
1368 by {
1369 if c == best_g && d == best_g {
1370 lemma_priority_lhs_monotone(scores@[best_g] as int,
1371 extra@[best_g] as int - 1, extra@[best_g] as int);
1372 } else if c == best_g {
1373 assert(priority_ge(scores@, extra_old, best_g, d));
1376 assert(extra@[d] == extra_old[d]);
1377 assert(extra@[best_g] as int == extra_old[best_g] as int + 1);
1378 } else if d == best_g {
1379 assert(scores@[c] as int * (2 * extra_old[best_g] as int + 1)
1382 >= scores@[best_g] as int * (2 * extra_old[c] as int - 1));
1383 lemma_priority_lhs_monotone(scores@[c] as int,
1384 extra_old[best_g] as int, extra@[best_g] as int);
1385 assert(extra@[c] == extra_old[c]);
1386 } else {
1387 assert(scores@[c] as int * (2 * extra_old[d] as int + 1)
1388 >= scores@[d] as int * (2 * extra_old[c] as int - 1));
1389 assert(extra@[c] == extra_old[c] && extra@[d] == extra_old[d]);
1390 }
1391 }
1392 let nv: u64 = (extra_old[best_g] + 1) as u64;
1393 lemma_sum_update(extra_old, best_g, nv, n as int);
1394 }
1395 awarded = awarded + 1;
1396 }
1397
1398 proof {
1399 assert(sum_to(extra@, n as int) + n as int == weight_total as int) by {
1400 assert(sum_to(extra@, n as int) == pool as int);
1401 }
1402 }
1403 CompetitiveSelectionSoft { extra, scores, weight_total, max_score }
1404 }
1405
1406 pub fn assign_next(&mut self) -> (winner: usize)
1411 requires
1412 old(self).mutable_score_inv(),
1413 (sum_to(old(self).extra@, old(self).extra@.len() as int) + old(self).extra@.len() as int)
1414 < (old(self).weight_total as int),
1415 ensures
1416 winner < old(self).scores.len(),
1417 priority_winner(old(self).scores@, old(self).extra@, winner as int),
1418 final(self).scores@ == old(self).scores@,
1419 final(self).weight_total == old(self).weight_total,
1420 final(self).max_score == old(self).max_score,
1421 final(self).extra@
1422 == old(self).extra@.update(
1423 winner as int,
1424 (old(self).extra@[winner as int] + 1) as u64,
1425 ),
1426 old(self).webster_allocation() ==> final(self).webster_allocation(),
1427 final(self).mutable_score_inv(),
1428 {
1429 let n = self.scores.len();
1430 let wtot = self.weight_total;
1431 let _ = wtot;
1432 let mut best: usize = 0;
1433 let mut j: usize = 1;
1434 while j < n
1435 invariant
1436 1 <= j <= n,
1437 best < j,
1438 n == self.scores.len(),
1439 self.extra.len() == n,
1440 self.mutable_score_inv(),
1441 wtot <= 1_000_000_000,
1442 forall|k: int| 0 <= k < n ==> #[trigger] self.scores@[k] <= 1_000_000_000,
1443 forall|k: int| 0 <= k < n ==> #[trigger] self.extra@[k] <= wtot,
1444 forall|c: int| 0 <= c < j ==> priority_ge(self.scores@, self.extra@, best as int, c),
1445 forall|c: int| 0 <= c < j
1446 && priority_equal(self.scores@, self.extra@, best as int, c)
1447 ==> best as int <= c,
1448 decreases n - j,
1449 {
1450 proof {
1451 assert((self.scores@[j as int] as int) * (2 * (self.extra@[best as int] as int) + 1)
1452 <= 1_000_000_000 * (2 * 1_000_000_000 + 1)) by (nonlinear_arith)
1453 requires
1454 self.scores@[j as int] as int <= 1_000_000_000,
1455 self.extra@[best as int] as int <= 1_000_000_000;
1456 assert((self.scores@[best as int] as int) * (2 * (self.extra@[j as int] as int) + 1)
1457 <= 1_000_000_000 * (2 * 1_000_000_000 + 1)) by (nonlinear_arith)
1458 requires
1459 self.scores@[best as int] as int <= 1_000_000_000,
1460 self.extra@[j as int] as int <= 1_000_000_000;
1461 }
1462 let lhs: u128 = (self.scores[j] as u128) * (2 * (self.extra[best] as u128) + 1);
1463 let rhs: u128 = (self.scores[best] as u128) * (2 * (self.extra[j] as u128) + 1);
1464 proof {
1465 assert(lhs as int == self.scores@[j as int] as int * (2 * self.extra@[best as int] as int + 1));
1466 assert(rhs as int == self.scores@[best as int] as int * (2 * self.extra@[j as int] as int + 1));
1467 }
1468 let old_best: usize = best;
1469 let old_j: usize = j;
1470 let _ = (old_best, old_j);
1471 if lhs > rhs {
1472 best = j;
1473 proof {
1474 assert(priority_ge(self.scores@, self.extra@, old_j as int, old_best as int));
1475 assert(priority_gt(self.scores@, self.extra@, old_j as int, old_best as int));
1476 assert forall|c: int| 0 <= c < old_j as int + 1
1477 implies priority_ge(self.scores@, self.extra@, best as int, c)
1478 by {
1479 if c == old_best as int {
1480 assert(priority_ge(self.scores@, self.extra@, old_j as int, old_best as int));
1481 } else if c < old_j as int {
1482 lemma_priority_ge_trans(self.scores@, self.extra@, old_j as int, old_best as int, c);
1483 } else {
1484 }
1486 }
1487 assert forall|c: int| 0 <= c < old_j as int + 1
1488 && priority_equal(self.scores@, self.extra@, best as int, c)
1489 implies best as int <= c
1490 by {
1491 if c < old_j as int {
1492 lemma_priority_gt_ge_not_equal(
1493 self.scores@, self.extra@,
1494 old_j as int, old_best as int, c,
1495 );
1496 assert(false);
1497 }
1498 }
1499 }
1500 } else {
1501 proof {
1502 assert(priority_ge(self.scores@, self.extra@, old_best as int, old_j as int));
1503 assert forall|c: int| 0 <= c < old_j as int + 1
1504 implies priority_ge(self.scores@, self.extra@, best as int, c)
1505 by {
1506 if c == old_j as int {
1507 assert(priority_ge(self.scores@, self.extra@, old_best as int, old_j as int));
1508 } else {
1509 }
1511 }
1512 assert forall|c: int| 0 <= c < old_j as int + 1
1513 && priority_equal(self.scores@, self.extra@, best as int, c)
1514 implies best as int <= c
1515 by {
1516 if c == old_j as int {
1517 assert(best < old_j);
1518 }
1519 }
1520 }
1521 }
1522 j = j + 1;
1523 }
1524 let ghost extra_old: Seq<u64> = self.extra@;
1525 let ghost best_g: int = best as int;
1526 proof {
1527 assert(self.scores@ == old(self).scores@);
1528 assert(extra_old == old(self).extra@);
1529 assert(priority_winner(self.scores@, extra_old, best_g));
1530 assert(old(self).score_order_preservation());
1531 assert(old(self).tie_boundedness());
1532 assert forall|c: int, d: int| 0 <= c < n && 0 <= d < n && self.scores@[c] < self.scores@[d]
1533 implies 1 + extra_old[c] as int <= 1 + extra_old[d] as int
1534 by {
1535 assert(old(self).weight(c) <= old(self).weight(d));
1536 }
1537 assert forall|c: int, d: int| 0 <= c < n && 0 <= d < n && self.scores@[c] == self.scores@[d]
1538 implies 1 + extra_old[c] as int <= 1 + extra_old[d] as int + 1
1539 by {
1540 assert(old(self).weight(c) <= old(self).weight(d) + 1);
1541 }
1542 }
1543 self.extra.set(best, self.extra[best] + 1);
1544 proof {
1545 assert forall|c: int, d: int| 0 <= c < n && 0 <= d < n && self.scores@[c] < self.scores@[d]
1546 implies 1 + self.extra@[c] as int <= 1 + self.extra@[d] as int
1547 by {
1548 if c == best_g && d == best_g {
1549 assert(false);
1550 } else if c == best_g {
1551 lemma_priority_strict(self.scores@, extra_old, best_g, d);
1552 } else if d == best_g {
1553 assert(1 + extra_old[c] as int <= 1 + extra_old[best_g] as int);
1554 } else {
1555 assert(1 + extra_old[c] as int <= 1 + extra_old[d] as int);
1556 }
1557 }
1558 assert forall|c: int, d: int| 0 <= c < n && 0 <= d < n && self.scores@[c] == self.scores@[d]
1559 implies 1 + self.extra@[c] as int <= 1 + self.extra@[d] as int + 1
1560 by {
1561 if c == best_g && d == best_g {
1562 assert(1 + extra_old[c] as int <= 1 + extra_old[d] as int + 1);
1563 } else if c == best_g {
1564 if self.scores@[best_g] > 0 {
1565 lemma_priority_tie(self.scores@, extra_old, best_g, d);
1566 } else {
1567 assert(1 + extra_old[c] as int <= 1 + extra_old[d] as int + 1);
1568 }
1569 } else if d == best_g {
1570 assert(1 + extra_old[c] as int <= 1 + extra_old[d] as int + 1);
1571 } else {
1572 assert(1 + extra_old[c] as int <= 1 + extra_old[d] as int + 1);
1573 }
1574 }
1575 if old(self).webster_allocation() {
1576 assert(self.webster_allocation()) by {
1577 assert forall|c: int, d: int|
1578 0 <= c < n && 0 <= d < n && self.extra@[c] >= 1
1579 implies self.scores@[c] as int * (2 * self.extra@[d] as int + 1)
1580 >= self.scores@[d] as int * (2 * self.extra@[c] as int - 1) by {
1581 if c == best_g && d == best_g {
1582 lemma_priority_lhs_monotone(
1583 self.scores@[best_g] as int,
1584 self.extra@[best_g] as int - 1,
1585 self.extra@[best_g] as int,
1586 );
1587 } else if c == best_g {
1588 assert(priority_ge(self.scores@, extra_old, best_g, d));
1589 assert(self.extra@[d] == extra_old[d]);
1590 assert(self.extra@[best_g] as int
1591 == extra_old[best_g] as int + 1);
1592 } else if d == best_g {
1593 assert(self.scores@[c] as int
1594 * (2 * extra_old[best_g] as int + 1)
1595 >= self.scores@[best_g] as int
1596 * (2 * extra_old[c] as int - 1));
1597 lemma_priority_lhs_monotone(
1598 self.scores@[c] as int,
1599 extra_old[best_g] as int,
1600 self.extra@[best_g] as int,
1601 );
1602 assert(self.extra@[c] == extra_old[c]);
1603 } else {
1604 assert(self.scores@[c] as int * (2 * extra_old[d] as int + 1)
1605 >= self.scores@[d] as int * (2 * extra_old[c] as int - 1));
1606 assert(self.extra@[c] == extra_old[c]
1607 && self.extra@[d] == extra_old[d]);
1608 }
1609 }
1610 }
1611 }
1612 let old_sum = sum_to(extra_old, n as int);
1613 lemma_sum_ge_one(extra_old, best_g, n as int);
1614 lemma_sum_update(extra_old, best_g, (extra_old[best_g] + 1) as u64, n as int);
1615 assert(sum_to(self.extra@, n as int) == old_sum + 1);
1616 assert(sum_to(self.extra@, n as int) + n as int <= self.weight_total as int);
1617 assert forall|k: int| 0 <= k < n
1618 implies self.extra@[k] <= self.weight_total by {
1619 if k == best_g {
1620 assert(extra_old[k] <= old_sum);
1621 }
1622 }
1623 assert(self.normalization_when_terminal()) by {
1624 if self.terminal() {
1625 assert(self.normalization());
1626 }
1627 }
1628 assert(self.mutable_score_inv());
1629 }
1630 best
1631 }
1632
1633 pub fn update_score(&mut self, c: usize, v: u64)
1637 requires
1638 old(self).mutable_score_inv(),
1639 c < old(self).scores.len(),
1640 1 <= v <= old(self).max_score,
1641 ensures
1642 final(self).weight_total == old(self).weight_total,
1643 final(self).max_score == old(self).max_score,
1644 final(self).scores@
1645 == old(self).scores@.update(c as int, v),
1646 final(self).extra@.len() == old(self).extra@.len(),
1647 forall|i: int| 0 <= i < final(self).extra.len() ==> final(self).extra@[i] == 0,
1648 final(self).mutable_score_inv(),
1649 {
1650 let ghost old_scores = self.scores@;
1651 self.scores.set(c, v);
1652 let ghost updated_scores = self.scores@;
1653 let n = self.extra.len();
1654 let mut i: usize = 0;
1655 while i < n
1656 invariant
1657 i <= n,
1658 n == self.extra.len(),
1659 n == self.scores.len(),
1660 self.scores@ == updated_scores,
1661 updated_scores == old_scores.update(c as int, v),
1662 self.weight_total == old(self).weight_total,
1663 self.max_score == old(self).max_score,
1664 forall|k: int| 0 <= k < i ==> self.extra@[k] == 0,
1665 forall|k: int| i <= k < n ==> self.extra@[k] == old(self).extra@[k],
1666 decreases n - i,
1667 {
1668 self.extra.set(i, 0);
1669 i = i + 1;
1670 }
1671 proof {
1672 lemma_sum_zero(self.extra@, n as int);
1673 assert(self.score_bounds()) by {
1674 assert forall|k: int| 0 <= k < n implies
1675 self.scores@[k] >= 1 && self.scores@[k] <= 1_000_000_000 by {
1676 if k != c as int {
1677 assert(self.scores@[k] == old(self).scores@[k]);
1678 }
1679 }
1680 }
1681 assert(self.score_order_preservation()) by {
1682 assert forall|a: int, b: int| 0 <= a < n && 0 <= b < n
1683 && self.scores@[a] < self.scores@[b]
1684 implies self.weight(a) <= self.weight(b) by {}
1685 }
1686 assert(self.tie_boundedness()) by {
1687 assert forall|a: int, b: int| 0 <= a < n && 0 <= b < n
1688 && self.scores@[a] == self.scores@[b]
1689 implies self.weight(a) <= self.weight(b) + 1 by {}
1690 }
1691 assert(self.normalization_when_terminal()) by {
1692 if self.terminal() {
1693 assert(self.normalization());
1694 }
1695 }
1696 assert(self.mutable_score_inv());
1697 }
1698 }
1699}
1700
1701pub open spec fn count_true(s: Seq<bool>, n: int) -> int
1705 decreases n,
1706{
1707 if n <= 0 { 0 } else if n > s.len() as int { 0 }
1708 else { (if s[n - 1] { 1int } else { 0int }) + count_true(s, n - 1) }
1709}
1710
1711pub proof fn lemma_count_set(s: Seq<bool>, m: int, n: int)
1713 requires 0 <= m < n <= s.len(), !s[m],
1714 ensures count_true(s.update(m, true), n) == count_true(s, n) + 1,
1715 decreases n,
1716{
1717 if n == m + 1 {
1718 lemma_count_unaffected(s, m, n - 1);
1719 } else {
1720 lemma_count_set(s, m, n - 1);
1721 }
1722}
1723
1724pub proof fn lemma_count_unaffected(s: Seq<bool>, m: int, p: int)
1726 requires 0 <= p <= m < s.len(),
1727 ensures count_true(s.update(m, true), p) == count_true(s, p),
1728 decreases p,
1729{
1730 if p > 0 {
1731 lemma_count_unaffected(s, m, p - 1);
1732 }
1733}
1734
1735pub proof fn lemma_count_zero(s: Seq<bool>, n: int)
1737 requires 0 <= n <= s.len(), forall|k: int| 0 <= k < n ==> !s[k],
1738 ensures count_true(s, n) == 0,
1739 decreases n,
1740{
1741 if n > 0 {
1742 lemma_count_zero(s, n - 1);
1743 }
1744}
1745
1746pub proof fn lemma_count_all_true(s: Seq<bool>, n: int)
1748 requires 0 <= n <= s.len(), forall|k: int| 0 <= k < n ==> s[k],
1749 ensures count_true(s, n) == n,
1750 decreases n,
1751{
1752 if n > 0 {
1753 lemma_count_all_true(s, n - 1);
1754 }
1755}
1756
1757pub proof fn lemma_count_upper(s: Seq<bool>, n: int)
1759 requires 0 <= n <= s.len(),
1760 ensures count_true(s, n) <= n,
1761 decreases n,
1762{
1763 if n > 0 {
1764 lemma_count_upper(s, n - 1);
1765 }
1766}
1767
1768pub struct CompetitiveSelectionRanked {
1770 pub scores: Vec<u64>,
1772 pub selected: Vec<bool>,
1774 pub k: usize,
1776 pub max_score: u64,
1778}
1779
1780impl CompetitiveSelectionRanked {
1781 pub open spec fn type_invariant(&self) -> bool {
1783 self.selected.len() == self.scores.len()
1784 && forall|i: int| 0 <= i < self.scores.len() ==>
1785 #[trigger] self.scores@[i] <= self.max_score
1786 }
1787
1788 pub open spec fn bounded_multiplicity(&self) -> bool {
1790 count_true(self.selected@, self.selected@.len() as int) <= self.k
1791 }
1792
1793 pub open spec fn threshold_optimality(&self) -> bool {
1795 forall|s: int, c: int|
1796 #![trigger self.selected@[s], self.selected@[c]]
1797 0 <= s < self.selected.len() && 0 <= c < self.selected.len()
1798 && self.selected@[s] && !self.selected@[c]
1799 ==> self.scores@[s] >= self.scores@[c]
1800 }
1801
1802 pub open spec fn ranked_tie_break(&self) -> bool {
1807 forall|s: int, c: int|
1808 #![trigger self.selected@[s], self.selected@[c]]
1809 0 <= s < self.selected.len() && 0 <= c < self.selected.len()
1810 && self.selected@[s] && !self.selected@[c]
1811 ==> (self.scores@[s] > self.scores@[c]
1812 || (self.scores@[s] == self.scores@[c] && s < c))
1813 }
1814
1815 pub open spec fn inv(&self) -> bool {
1817 self.type_invariant() && self.bounded_multiplicity()
1818 && self.threshold_optimality() && self.ranked_tie_break()
1819 }
1820
1821 pub fn new(scores: Vec<u64>, k: usize, max_score: u64) -> (r: CompetitiveSelectionRanked)
1823 requires
1824 forall|i: int| 0 <= i < scores.len() ==> #[trigger] scores@[i] <= max_score,
1825 ensures
1826 r.scores@ == scores@,
1827 r.k == k,
1828 r.max_score == max_score,
1829 r.selected@.len() == scores@.len(),
1830 forall|j: int| 0 <= j < r.selected@.len() ==> !r.selected@[j],
1831 r.inv(),
1832 {
1833 let n = scores.len();
1834 let mut selected: Vec<bool> = Vec::new();
1835 let mut i: usize = 0;
1836 while i < n
1837 invariant
1838 i <= n,
1839 selected.len() == i,
1840 forall|j: int| 0 <= j < i ==> !selected@[j],
1841 decreases n - i,
1842 {
1843 selected.push(false);
1844 i = i + 1;
1845 }
1846 proof { lemma_count_zero(selected@, selected@.len() as int); }
1847 CompetitiveSelectionRanked { scores, selected, k, max_score }
1848 }
1849
1850 fn find_max_unselected(&self) -> (r: Option<usize>)
1852 requires self.type_invariant(),
1853 ensures
1854 r is None ==> (forall|c: int| 0 <= c < self.selected.len() ==> self.selected@[c]),
1855 r matches Option::Some(m) ==> (m < self.scores.len() && !self.selected@[m as int]
1856 && (forall|c: int| 0 <= c < self.scores.len() && !self.selected@[c]
1857 ==> #[trigger] self.scores@[c] <= self.scores@[m as int])
1858 && (forall|c: int| 0 <= c < self.scores.len() && !self.selected@[c]
1859 && #[trigger] self.scores@[c] == self.scores@[m as int]
1860 ==> m as int <= c)),
1861 {
1862 let n = self.scores.len();
1863 let mut best: Option<usize> = None;
1864 let mut i: usize = 0;
1865 while i < n
1866 invariant
1867 i <= n,
1868 n == self.scores.len(),
1869 self.selected.len() == n,
1870 best is None ==> (forall|c: int| 0 <= c < i ==> self.selected@[c]),
1871 best matches Option::Some(m) ==> (m < i && !self.selected@[m as int]
1872 && (forall|c: int| 0 <= c < i && !self.selected@[c]
1873 ==> #[trigger] self.scores@[c] <= self.scores@[m as int])
1874 && (forall|c: int| 0 <= c < i && !self.selected@[c]
1875 && #[trigger] self.scores@[c] == self.scores@[m as int]
1876 ==> m as int <= c)),
1877 decreases n - i,
1878 {
1879 if !self.selected[i] {
1880 match best {
1881 Option::Some(m) => {
1882 if self.scores[i] > self.scores[m] {
1883 best = Some(i);
1884 }
1885 }
1886 Option::None => {
1887 best = Some(i);
1888 }
1889 }
1890 }
1891 i = i + 1;
1892 }
1893 best
1894 }
1895
1896 pub fn select(&mut self)
1899 requires old(self).type_invariant(),
1900 ensures
1901 final(self).scores@ == old(self).scores@,
1902 final(self).k == old(self).k,
1903 final(self).max_score == old(self).max_score,
1904 count_true(final(self).selected@, final(self).selected@.len() as int)
1905 == if final(self).k < final(self).selected.len() {
1906 final(self).k as int
1907 } else {
1908 final(self).selected.len() as int
1909 },
1910 final(self).inv(),
1911 {
1912 let n = self.scores.len();
1913 let original_len = self.selected.len();
1914 let original_k = self.k;
1915 let _ = (original_len, original_k);
1916 assert(original_len == n);
1917 let mut i: usize = 0;
1919 while i < n
1920 invariant
1921 i <= n,
1922 n == self.scores.len(),
1923 self.selected.len() == n,
1924 self.type_invariant(),
1925 self.scores@ == old(self).scores@,
1926 self.k == old(self).k,
1927 self.max_score == old(self).max_score,
1928 original_len == n,
1929 original_k == self.k,
1930 forall|j: int| 0 <= j < i ==> !self.selected@[j],
1931 decreases n - i,
1932 {
1933 self.selected.set(i, false);
1934 i = i + 1;
1935 }
1936 proof { lemma_count_zero(self.selected@, n as int); }
1937 let mut round: usize = 0;
1939 while round < self.k
1940 invariant
1941 n == self.scores.len(),
1942 self.selected.len() == n,
1943 self.type_invariant(),
1944 self.scores@ == old(self).scores@,
1945 self.k == old(self).k,
1946 self.max_score == old(self).max_score,
1947 original_len == n,
1948 original_k == self.k,
1949 round <= self.k,
1950 count_true(self.selected@, n as int) == round,
1951 self.threshold_optimality(),
1952 self.ranked_tie_break(),
1953 decreases self.k - round,
1954 {
1955 let m_opt = self.find_max_unselected();
1956 match m_opt {
1957 Option::Some(m) => {
1958 let ghost s0 = self.selected@;
1959 assert forall|c: int| 0 <= c < n && !s0[c]
1960 implies self.scores@[c] <= self.scores@[m as int] by {}
1961 assert forall|c: int| 0 <= c < n && !s0[c]
1962 && self.scores@[c] == self.scores@[m as int]
1963 implies m as int <= c by {}
1964 proof { lemma_count_set(s0, m as int, n as int); }
1965 self.selected.set(m, true);
1966 proof {
1967 assert(self.threshold_optimality()) by {
1968 assert forall|s: int, c: int|
1969 #![trigger self.selected@[s], self.selected@[c]]
1970 0 <= s < n && 0 <= c < n && self.selected@[s] && !self.selected@[c]
1971 implies self.scores@[s] >= self.scores@[c] by {
1972 if s != m as int {
1973 assert(s0[s]);
1974 }
1975 }
1976 }
1977 assert(self.ranked_tie_break()) by {
1978 assert forall|s: int, c: int|
1979 #![trigger self.selected@[s], self.selected@[c]]
1980 0 <= s < n && 0 <= c < n
1981 && self.selected@[s] && !self.selected@[c]
1982 implies self.scores@[s] > self.scores@[c]
1983 || (self.scores@[s] == self.scores@[c] && s < c) by {
1984 if s != m as int {
1985 assert(s0[s]);
1986 assert(!s0[c]);
1987 } else {
1988 assert(!s0[c]);
1989 assert(c != m as int);
1990 if self.scores@[m as int] == self.scores@[c] {
1991 assert(m as int <= c);
1992 assert((m as int) < c);
1993 }
1994 }
1995 }
1996 }
1997 }
1998 }
1999 Option::None => {
2000 proof {
2001 lemma_count_all_true(self.selected@, n as int);
2002 assert(count_true(self.selected@, n as int) == n as int);
2003 assert(round as int == n as int);
2004 assert(round < self.k);
2005 assert(n < self.k);
2006 assert(original_len == n);
2007 assert(original_k == self.k);
2008 assert(!(original_k < original_len));
2009 }
2010 return;
2011 }
2012 }
2013 round = round + 1;
2014 }
2015 proof {
2016 lemma_count_upper(self.selected@, n as int);
2017 assert(round == self.k);
2018 assert(count_true(self.selected@, n as int) == self.k as int);
2019 assert(self.k <= n);
2020 }
2021 }
2022
2023 pub fn update_scores(&mut self, new_scores: Vec<u64>)
2025 requires
2026 old(self).type_invariant(),
2027 new_scores.len() == old(self).scores.len(),
2028 forall|i: int| 0 <= i < new_scores.len() ==>
2029 #[trigger] new_scores@[i] <= old(self).max_score,
2030 ensures
2031 final(self).scores@ == new_scores@,
2032 final(self).k == old(self).k,
2033 final(self).max_score == old(self).max_score,
2034 forall|i: int| 0 <= i < final(self).selected.len() ==>
2035 !final(self).selected@[i],
2036 final(self).inv(),
2037 {
2038 let ghost ns = new_scores@;
2039 let n = new_scores.len();
2040 self.scores = new_scores;
2041 let mut i: usize = 0;
2042 while i < n
2043 invariant
2044 i <= n,
2045 n == self.scores.len(),
2046 self.selected.len() == n,
2047 self.scores@ == ns,
2048 self.k == old(self).k,
2049 self.max_score == old(self).max_score,
2050 forall|j: int| 0 <= j < i ==> !self.selected@[j],
2051 decreases n - i,
2052 {
2053 self.selected.set(i, false);
2054 i = i + 1;
2055 }
2056 proof { lemma_count_zero(self.selected@, n as int); }
2057 }
2058}
2059
2060}