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 proof fn lemma_priority_strict(scores: Seq<u64>, extra: Seq<u64>, a: int, b: int)
807 requires
808 priority_ge(scores, extra, a, b),
809 scores[a] < scores[b],
810 ensures
811 extra[a] < extra[b],
812{
813 if extra[a] >= extra[b] {
814 assert(scores[b] as int * (2 * extra[a] as int + 1)
815 > scores[a] as int * (2 * extra[b] as int + 1)) by (nonlinear_arith)
816 requires scores[a] < scores[b], extra[a] >= extra[b], scores[a] >= 0, scores[b] >= 0,
817 extra[a] >= 0, extra[b] >= 0;
818 assert(false);
819 }
820}
821
822pub proof fn lemma_priority_tie(scores: Seq<u64>, extra: Seq<u64>, a: int, b: int)
825 requires
826 priority_ge(scores, extra, a, b),
827 scores[a] == scores[b],
828 scores[a] > 0,
829 ensures
830 extra[a] <= extra[b],
831{
832 if extra[a] > extra[b] {
833 assert(scores[b] as int * (2 * extra[a] as int + 1)
834 > scores[a] as int * (2 * extra[b] as int + 1)) by (nonlinear_arith)
835 requires scores[a] == scores[b], scores[a] > 0, extra[a] > extra[b],
836 extra[a] >= 0, extra[b] >= 0;
837 assert(false);
838 }
839}
840
841pub proof fn lemma_priority_ge_trans(scores: Seq<u64>, extra: Seq<u64>, a: int, b: int, c: int)
850 requires
851 priority_ge(scores, extra, a, b),
852 priority_ge(scores, extra, b, c),
853 ensures
854 priority_ge(scores, extra, a, c),
855{
856 let sa = scores[a] as int;
857 let sb = scores[b] as int;
858 let sc = scores[c] as int;
859 let a_den = 2 * extra[a] as int + 1;
860 let b_den = 2 * extra[b] as int + 1;
861 let c_den = 2 * extra[c] as int + 1;
862 assert(sa * b_den * c_den >= sb * a_den * c_den) by (nonlinear_arith)
863 requires sa * b_den >= sb * a_den, c_den >= 0;
864 assert(sb * c_den * a_den >= sc * b_den * a_den) by (nonlinear_arith)
865 requires sb * c_den >= sc * b_den, a_den >= 0;
866 assert(sa * b_den * c_den >= sc * b_den * a_den) by (nonlinear_arith)
867 requires
868 sa * b_den * c_den >= sb * a_den * c_den,
869 sb * c_den * a_den >= sc * b_den * a_den;
870 assert(sa * c_den >= sc * a_den) by (nonlinear_arith)
871 requires
872 sa * b_den * c_den >= sc * b_den * a_den,
873 b_den > 0;
874}
875
876pub proof fn lemma_priority_lhs_monotone(s: int, e: int, f: int)
880 requires s >= 0, e <= f,
881 ensures s * (2 * e + 1) <= s * (2 * f + 1),
882{
883 assert(s * (2 * e + 1) <= s * (2 * f + 1)) by (nonlinear_arith)
884 requires s >= 0, e <= f;
885}
886
887pub struct CompetitiveSelectionSoft {
890 pub extra: Vec<u64>,
892 pub scores: Vec<u64>,
894 pub weight_total: u64,
896 pub max_score: u64,
898}
899
900impl CompetitiveSelectionSoft {
901 pub open spec fn well_formed(&self) -> bool {
903 self.extra.len() == self.scores.len() && self.extra.len() > 0
904 }
905
906 pub open spec fn score_bounds(&self) -> bool {
908 forall|i: int| 0 <= i < self.scores.len() ==>
909 #[trigger] self.scores@[i] >= 1 && self.scores@[i] <= self.max_score
910 }
911
912 pub open spec fn weight(&self, i: int) -> int {
915 1 + self.extra@[i] as int
916 }
917
918 pub open spec fn universal_contribution(&self) -> bool {
920 forall|i: int| 0 <= i < self.extra.len() ==> #[trigger] self.weight(i) > 0
921 }
922
923 pub open spec fn score_order_preservation(&self) -> bool {
926 forall|i: int, j: int|
927 0 <= i < self.scores.len() && 0 <= j < self.scores.len()
928 && self.scores@[i] < self.scores@[j]
929 ==> #[trigger] self.weight(i) <= #[trigger] self.weight(j)
930 }
931
932 pub open spec fn tie_boundedness(&self) -> bool {
934 forall|i: int, j: int|
935 0 <= i < self.scores.len() && 0 <= j < self.scores.len()
936 && self.scores@[i] == self.scores@[j]
937 ==> #[trigger] self.weight(i) <= #[trigger] self.weight(j) + 1
938 }
939
940 pub open spec fn normalization(&self) -> bool {
942 sum_to(self.extra@, self.extra@.len() as int) + self.extra@.len() as int
943 == self.weight_total as int
944 }
945
946 pub open spec fn bounded_total(&self) -> bool {
948 sum_to(self.extra@, self.extra@.len() as int) + self.extra@.len() as int
949 <= self.weight_total as int
950 }
951
952 pub open spec fn terminal(&self) -> bool {
954 sum_to(self.extra@, self.extra@.len() as int)
955 == self.weight_total as int - self.extra@.len() as int
956 }
957
958 pub open spec fn normalization_when_terminal(&self) -> bool {
960 self.terminal() ==> self.normalization()
961 }
962
963 pub open spec fn bounded(&self) -> bool {
967 self.weight_total <= 1_000_000_000
968 && self.max_score <= 1_000_000_000
969 && forall|k: int| 0 <= k < self.extra.len() ==> #[trigger] self.extra@[k] <= self.weight_total
970 }
971
972 pub open spec fn mutable_score_inv(&self) -> bool {
975 self.well_formed()
976 && self.weight_total as int >= self.extra.len() as int
977 && self.score_bounds()
978 && self.bounded()
979 && self.bounded_total()
980 && self.normalization_when_terminal()
981 && self.universal_contribution()
982 && self.score_order_preservation()
983 && self.tie_boundedness()
984 }
985
986 pub open spec fn webster_allocation(&self) -> bool {
993 forall|i: int, j: int| #![trigger self.extra@[i], self.extra@[j]]
994 0 <= i < self.extra.len() && 0 <= j < self.extra.len()
995 && self.extra@[i] >= 1
996 ==> self.scores@[i] as int * (2 * self.extra@[j] as int + 1)
997 >= self.scores@[j] as int * (2 * self.extra@[i] as int - 1)
998 }
999
1000 pub open spec fn inv(&self) -> bool {
1002 self.mutable_score_inv() && self.normalization()
1003 }
1004
1005 pub fn weight_at(&self, i: usize) -> (w: u64)
1007 requires i < self.extra.len(), self.bounded(),
1008 ensures w as int == self.weight(i as int),
1009 {
1010 self.extra[i] + 1
1011 }
1012
1013 pub fn assigned_weight(&self) -> (total: u64)
1015 requires self.mutable_score_inv(),
1016 ensures
1017 total as int
1018 == sum_to(self.extra@, self.extra@.len() as int) + self.extra@.len() as int,
1019 total <= self.weight_total,
1020 {
1021 let len = self.extra.len();
1022 let mut total = len as u64;
1023 let mut index: usize = 0;
1024 proof {
1025 assert(self.bounded_total());
1026 }
1027 while index < len
1028 invariant
1029 index <= len,
1030 len == self.extra.len(),
1031 total as int == sum_to(self.extra@, index as int) + len as int,
1032 total <= self.weight_total,
1033 sum_to(self.extra@, len as int) + len as int <= self.weight_total as int,
1034 decreases len - index,
1035 {
1036 proof {
1037 assert(sum_to(self.extra@, index as int + 1)
1038 == self.extra@[index as int] as int
1039 + sum_to(self.extra@, index as int));
1040 lemma_sum_prefix_le(self.extra@, index as int + 1, len as int);
1041 assert(sum_to(self.extra@, index as int + 1) + len as int
1042 <= self.weight_total as int);
1043 assert(total as int + self.extra@[index as int] as int
1044 <= self.weight_total as int);
1045 }
1046 total = total + self.extra[index];
1047 index += 1;
1048 }
1049 total
1050 }
1051
1052 pub fn init(scores: Vec<u64>, weight_total: u64, max_score: u64) -> (s: CompetitiveSelectionSoft)
1056 requires
1057 scores.len() >= 1,
1058 weight_total >= scores.len() as u64,
1059 weight_total <= 1_000_000_000,
1060 max_score <= 1_000_000_000,
1061 forall|i: int| 0 <= i < scores.len() ==>
1062 #[trigger] scores@[i] >= 1 && scores@[i] <= max_score,
1063 ensures
1064 s.scores@ == scores@,
1065 s.weight_total == weight_total,
1066 s.max_score == max_score,
1067 s.extra@.len() == scores@.len(),
1068 forall|i: int| 0 <= i < s.extra.len() ==> s.extra@[i] == 0,
1069 s.mutable_score_inv(),
1070 {
1071 let n = scores.len();
1072 let mut extra: Vec<u64> = Vec::new();
1073 let mut i: usize = 0;
1074 while i < n
1075 invariant
1076 i <= n,
1077 extra.len() == i,
1078 forall|k: int| 0 <= k < i ==> extra@[k] == 0,
1079 sum_to(extra@, i as int) == 0,
1080 decreases n - i,
1081 {
1082 let ghost eold = extra@;
1083 extra.push(0);
1084 proof {
1085 lemma_sum_push_prefix(eold, 0, i as int);
1086 assert(sum_to(extra@, i as int + 1)
1087 == extra@[i as int] as int + sum_to(extra@, i as int));
1088 }
1089 i = i + 1;
1090 }
1091 proof {
1092 assert forall|a: int, b: int| 0 <= a < n && 0 <= b < n
1093 && scores@[a] < scores@[b]
1094 implies 1 + extra@[a] as int <= 1 + extra@[b] as int by {}
1095 assert forall|a: int, b: int| 0 <= a < n && 0 <= b < n
1096 && scores@[a] == scores@[b]
1097 implies 1 + extra@[a] as int <= 1 + extra@[b] as int + 1 by {}
1098 }
1099 CompetitiveSelectionSoft { extra, scores, weight_total, max_score }
1100 }
1101
1102 pub fn new(scores: Vec<u64>, weight_total: u64, max_score: u64) -> (s: CompetitiveSelectionSoft)
1107 requires
1108 scores.len() >= 1,
1109 weight_total >= scores.len() as u64,
1110 weight_total <= 1_000_000_000,
1111 max_score <= 1_000_000_000,
1112 forall|i: int| 0 <= i < scores.len() ==> #[trigger] scores@[i] >= 1 && scores@[i] <= max_score,
1113 ensures
1114 s.scores@ == scores@,
1115 s.weight_total == weight_total,
1116 s.max_score == max_score,
1117 s.mutable_score_inv(),
1118 s.inv(),
1119 s.webster_allocation(),
1120 {
1121 let n = scores.len();
1122 let pool: u64 = weight_total - (n as u64);
1123 let mut extra: Vec<u64> = Vec::new();
1124 let mut i: usize = 0;
1125 while i < n
1126 invariant
1127 i <= n,
1128 extra.len() == i,
1129 forall|k: int| 0 <= k < i ==> extra@[k] == 0,
1130 sum_to(extra@, i as int) == 0,
1131 decreases n - i,
1132 {
1133 let ghost eold = extra@;
1134 extra.push(0);
1135 proof {
1136 lemma_sum_push_prefix(eold, 0, i as int);
1137 assert(sum_to(extra@, i as int + 1) == extra@[i as int] as int + sum_to(extra@, i as int));
1138 }
1139 i = i + 1;
1140 }
1141
1142 let mut awarded: u64 = 0;
1143 while awarded < pool
1144 invariant
1145 extra.len() == n,
1146 n == scores.len(),
1147 n >= 1,
1148 weight_total >= n as u64,
1149 weight_total <= 1_000_000_000,
1150 pool == weight_total - (n as u64),
1151 awarded <= pool,
1152 forall|k: int| 0 <= k < n ==> #[trigger] scores@[k] >= 1 && scores@[k] <= 1_000_000_000,
1153 sum_to(extra@, n as int) == awarded as int,
1154 forall|k: int| 0 <= k < n ==> #[trigger] extra@[k] <= awarded,
1155 forall|c: int, d: int|
1156 0 <= c < n && 0 <= d < n && scores@[c] < scores@[d]
1157 ==> 1 + extra@[c] as int <= 1 + extra@[d] as int,
1158 forall|c: int, d: int|
1159 0 <= c < n && 0 <= d < n && scores@[c] == scores@[d]
1160 ==> 1 + extra@[c] as int <= 1 + extra@[d] as int + 1,
1161 forall|c: int, d: int|
1167 0 <= c < n && 0 <= d < n && extra@[c] >= 1
1168 ==> scores@[c] as int * (2 * extra@[d] as int + 1)
1169 >= scores@[d] as int * (2 * extra@[c] as int - 1),
1170 decreases pool - awarded,
1171 {
1172 let mut best: usize = 0;
1173 let mut j: usize = 1;
1174 while j < n
1175 invariant
1176 1 <= j <= n,
1177 best < j,
1178 extra.len() == n,
1179 n == scores.len(),
1180 weight_total <= 1_000_000_000,
1181 pool == weight_total - (n as u64),
1182 awarded <= pool,
1183 forall|k: int| 0 <= k < n ==> #[trigger] scores@[k] <= 1_000_000_000,
1184 forall|k: int| 0 <= k < n ==> #[trigger] extra@[k] <= awarded,
1185 forall|c: int| 0 <= c < j ==> priority_ge(scores@, extra@, best as int, c),
1186 decreases n - j,
1187 {
1188 proof {
1189 assert((scores@[j as int] as int) * (2 * (extra@[best as int] as int) + 1)
1190 <= 1_000_000_000 * (2 * 1_000_000_000 + 1)) by (nonlinear_arith)
1191 requires
1192 scores@[j as int] as int <= 1_000_000_000,
1193 extra@[best as int] as int <= 1_000_000_000;
1194 assert((scores@[best as int] as int) * (2 * (extra@[j as int] as int) + 1)
1195 <= 1_000_000_000 * (2 * 1_000_000_000 + 1)) by (nonlinear_arith)
1196 requires
1197 scores@[best as int] as int <= 1_000_000_000,
1198 extra@[j as int] as int <= 1_000_000_000;
1199 }
1200 let lhs: u128 = (scores[j] as u128) * (2 * (extra[best] as u128) + 1);
1201 let rhs: u128 = (scores[best] as u128) * (2 * (extra[j] as u128) + 1);
1202 proof {
1203 assert(lhs as int == scores@[j as int] as int * (2 * extra@[best as int] as int + 1));
1204 assert(rhs as int == scores@[best as int] as int * (2 * extra@[j as int] as int + 1));
1205 }
1206 let old_best: usize = best;
1207 let old_j: usize = j;
1208 let _ = (old_best, old_j);
1209 if lhs > rhs {
1210 best = j;
1211 proof {
1212 assert(priority_ge(scores@, extra@, old_j as int, old_best as int));
1213 assert forall|c: int| 0 <= c < old_j as int + 1
1214 implies priority_ge(scores@, extra@, best as int, c)
1215 by {
1216 if c == old_best as int {
1217 assert(priority_ge(scores@, extra@, old_j as int, old_best as int));
1218 } else if c < old_j as int {
1219 lemma_priority_ge_trans(scores@, extra@, old_j as int, old_best as int, c);
1220 } else {
1221 }
1223 }
1224 }
1225 } else {
1226 proof {
1227 assert(priority_ge(scores@, extra@, old_best as int, old_j as int));
1228 assert forall|c: int| 0 <= c < old_j as int + 1
1229 implies priority_ge(scores@, extra@, best as int, c)
1230 by {
1231 if c == old_j as int {
1232 assert(priority_ge(scores@, extra@, old_best as int, old_j as int));
1233 } else {
1234 }
1236 }
1237 }
1238 }
1239 j = j + 1;
1240 }
1241 let ghost extra_old: Seq<u64> = extra@;
1242 let ghost best_g: int = best as int;
1243 proof {
1248 assert forall|c: int, d: int| 0 <= c < n && 0 <= d < n && scores@[c] < scores@[d]
1249 implies 1 + extra_old[c] as int <= 1 + extra_old[d] as int
1250 by {}
1251 assert forall|c: int, d: int| 0 <= c < n && 0 <= d < n && scores@[c] == scores@[d]
1252 implies 1 + extra_old[c] as int <= 1 + extra_old[d] as int + 1
1253 by {}
1254 assert forall|c: int, d: int| 0 <= c < n && 0 <= d < n && extra_old[c] >= 1
1255 implies scores@[c] as int * (2 * extra_old[d] as int + 1)
1256 >= scores@[d] as int * (2 * extra_old[c] as int - 1)
1257 by {}
1258 assert forall|c: int| 0 <= c < n
1259 implies priority_ge(scores@, extra_old, best_g, c)
1260 by {}
1261 }
1262 extra.set(best, extra[best] + 1);
1263 proof {
1264 assert forall|c: int, d: int| 0 <= c < n && 0 <= d < n && scores@[c] < scores@[d]
1265 implies 1 + extra@[c] as int <= 1 + extra@[d] as int
1266 by {
1267 if c == best_g && d == best_g {
1268 assert(false);
1269 } else if c == best_g {
1270 lemma_priority_strict(scores@, extra_old, best_g, d);
1271 } else if d == best_g {
1272 assert(1 + extra_old[c] as int <= 1 + extra_old[best_g] as int);
1273 } else {
1274 assert(1 + extra_old[c] as int <= 1 + extra_old[d] as int);
1275 }
1276 }
1277 assert forall|c: int, d: int| 0 <= c < n && 0 <= d < n && scores@[c] == scores@[d]
1278 implies 1 + extra@[c] as int <= 1 + extra@[d] as int + 1
1279 by {
1280 if c == best_g && d == best_g {
1281 assert(1 + extra_old[c] as int <= 1 + extra_old[d] as int + 1);
1282 } else if c == best_g {
1283 if scores@[best_g] > 0 {
1284 lemma_priority_tie(scores@, extra_old, best_g, d);
1285 } else {
1286 assert(1 + extra_old[c] as int <= 1 + extra_old[d] as int + 1);
1287 }
1288 } else if d == best_g {
1289 assert(1 + extra_old[c] as int <= 1 + extra_old[d] as int + 1);
1290 } else {
1291 assert(1 + extra_old[c] as int <= 1 + extra_old[d] as int + 1);
1292 }
1293 }
1294 assert forall|c: int, d: int| 0 <= c < n && 0 <= d < n && extra@[c] >= 1
1296 implies scores@[c] as int * (2 * extra@[d] as int + 1)
1297 >= scores@[d] as int * (2 * extra@[c] as int - 1)
1298 by {
1299 if c == best_g && d == best_g {
1300 lemma_priority_lhs_monotone(scores@[best_g] as int,
1301 extra@[best_g] as int - 1, extra@[best_g] as int);
1302 } else if c == best_g {
1303 assert(priority_ge(scores@, extra_old, best_g, d));
1306 assert(extra@[d] == extra_old[d]);
1307 assert(extra@[best_g] as int == extra_old[best_g] as int + 1);
1308 } else if d == best_g {
1309 assert(scores@[c] as int * (2 * extra_old[best_g] as int + 1)
1312 >= scores@[best_g] as int * (2 * extra_old[c] as int - 1));
1313 lemma_priority_lhs_monotone(scores@[c] as int,
1314 extra_old[best_g] as int, extra@[best_g] as int);
1315 assert(extra@[c] == extra_old[c]);
1316 } else {
1317 assert(scores@[c] as int * (2 * extra_old[d] as int + 1)
1318 >= scores@[d] as int * (2 * extra_old[c] as int - 1));
1319 assert(extra@[c] == extra_old[c] && extra@[d] == extra_old[d]);
1320 }
1321 }
1322 let nv: u64 = (extra_old[best_g] + 1) as u64;
1323 lemma_sum_update(extra_old, best_g, nv, n as int);
1324 }
1325 awarded = awarded + 1;
1326 }
1327
1328 proof {
1329 assert(sum_to(extra@, n as int) + n as int == weight_total as int) by {
1330 assert(sum_to(extra@, n as int) == pool as int);
1331 }
1332 }
1333 CompetitiveSelectionSoft { extra, scores, weight_total, max_score }
1334 }
1335
1336 pub fn assign_next(&mut self) -> (winner: usize)
1341 requires
1342 old(self).mutable_score_inv(),
1343 (sum_to(old(self).extra@, old(self).extra@.len() as int) + old(self).extra@.len() as int)
1344 < (old(self).weight_total as int),
1345 ensures
1346 winner < old(self).scores.len(),
1347 priority_winner(old(self).scores@, old(self).extra@, winner as int),
1348 final(self).scores@ == old(self).scores@,
1349 final(self).weight_total == old(self).weight_total,
1350 final(self).max_score == old(self).max_score,
1351 final(self).extra@
1352 == old(self).extra@.update(
1353 winner as int,
1354 (old(self).extra@[winner as int] + 1) as u64,
1355 ),
1356 final(self).mutable_score_inv(),
1357 {
1358 let n = self.scores.len();
1359 let wtot = self.weight_total;
1360 let _ = wtot;
1361 let mut best: usize = 0;
1362 let mut j: usize = 1;
1363 while j < n
1364 invariant
1365 1 <= j <= n,
1366 best < j,
1367 n == self.scores.len(),
1368 self.extra.len() == n,
1369 self.mutable_score_inv(),
1370 wtot <= 1_000_000_000,
1371 forall|k: int| 0 <= k < n ==> #[trigger] self.scores@[k] <= 1_000_000_000,
1372 forall|k: int| 0 <= k < n ==> #[trigger] self.extra@[k] <= wtot,
1373 forall|c: int| 0 <= c < j ==> priority_ge(self.scores@, self.extra@, best as int, c),
1374 forall|c: int| 0 <= c < j
1375 && priority_equal(self.scores@, self.extra@, best as int, c)
1376 ==> best as int <= c,
1377 decreases n - j,
1378 {
1379 proof {
1380 assert((self.scores@[j as int] as int) * (2 * (self.extra@[best as int] as int) + 1)
1381 <= 1_000_000_000 * (2 * 1_000_000_000 + 1)) by (nonlinear_arith)
1382 requires
1383 self.scores@[j as int] as int <= 1_000_000_000,
1384 self.extra@[best as int] as int <= 1_000_000_000;
1385 assert((self.scores@[best as int] as int) * (2 * (self.extra@[j as int] as int) + 1)
1386 <= 1_000_000_000 * (2 * 1_000_000_000 + 1)) by (nonlinear_arith)
1387 requires
1388 self.scores@[best as int] as int <= 1_000_000_000,
1389 self.extra@[j as int] as int <= 1_000_000_000;
1390 }
1391 let lhs: u128 = (self.scores[j] as u128) * (2 * (self.extra[best] as u128) + 1);
1392 let rhs: u128 = (self.scores[best] as u128) * (2 * (self.extra[j] as u128) + 1);
1393 proof {
1394 assert(lhs as int == self.scores@[j as int] as int * (2 * self.extra@[best as int] as int + 1));
1395 assert(rhs as int == self.scores@[best as int] as int * (2 * self.extra@[j as int] as int + 1));
1396 }
1397 let old_best: usize = best;
1398 let old_j: usize = j;
1399 let _ = (old_best, old_j);
1400 if lhs > rhs {
1401 best = j;
1402 proof {
1403 assert(priority_ge(self.scores@, self.extra@, old_j as int, old_best as int));
1404 assert(priority_gt(self.scores@, self.extra@, old_j as int, old_best as int));
1405 assert forall|c: int| 0 <= c < old_j as int + 1
1406 implies priority_ge(self.scores@, self.extra@, best as int, c)
1407 by {
1408 if c == old_best as int {
1409 assert(priority_ge(self.scores@, self.extra@, old_j as int, old_best as int));
1410 } else if c < old_j as int {
1411 lemma_priority_ge_trans(self.scores@, self.extra@, old_j as int, old_best as int, c);
1412 } else {
1413 }
1415 }
1416 assert forall|c: int| 0 <= c < old_j as int + 1
1417 && priority_equal(self.scores@, self.extra@, best as int, c)
1418 implies best as int <= c
1419 by {
1420 if c < old_j as int {
1421 lemma_priority_gt_ge_not_equal(
1422 self.scores@, self.extra@,
1423 old_j as int, old_best as int, c,
1424 );
1425 assert(false);
1426 }
1427 }
1428 }
1429 } else {
1430 proof {
1431 assert(priority_ge(self.scores@, self.extra@, old_best as int, old_j as int));
1432 assert forall|c: int| 0 <= c < old_j as int + 1
1433 implies priority_ge(self.scores@, self.extra@, best as int, c)
1434 by {
1435 if c == old_j as int {
1436 assert(priority_ge(self.scores@, self.extra@, old_best as int, old_j as int));
1437 } else {
1438 }
1440 }
1441 assert forall|c: int| 0 <= c < old_j as int + 1
1442 && priority_equal(self.scores@, self.extra@, best as int, c)
1443 implies best as int <= c
1444 by {
1445 if c == old_j as int {
1446 assert(best < old_j);
1447 }
1448 }
1449 }
1450 }
1451 j = j + 1;
1452 }
1453 let ghost extra_old: Seq<u64> = self.extra@;
1454 let ghost best_g: int = best as int;
1455 proof {
1456 assert(self.scores@ == old(self).scores@);
1457 assert(extra_old == old(self).extra@);
1458 assert(priority_winner(self.scores@, extra_old, best_g));
1459 assert(old(self).score_order_preservation());
1460 assert(old(self).tie_boundedness());
1461 assert forall|c: int, d: int| 0 <= c < n && 0 <= d < n && self.scores@[c] < self.scores@[d]
1462 implies 1 + extra_old[c] as int <= 1 + extra_old[d] as int
1463 by {
1464 assert(old(self).weight(c) <= old(self).weight(d));
1465 }
1466 assert forall|c: int, d: int| 0 <= c < n && 0 <= d < n && self.scores@[c] == self.scores@[d]
1467 implies 1 + extra_old[c] as int <= 1 + extra_old[d] as int + 1
1468 by {
1469 assert(old(self).weight(c) <= old(self).weight(d) + 1);
1470 }
1471 }
1472 self.extra.set(best, self.extra[best] + 1);
1473 proof {
1474 assert forall|c: int, d: int| 0 <= c < n && 0 <= d < n && self.scores@[c] < self.scores@[d]
1475 implies 1 + self.extra@[c] as int <= 1 + self.extra@[d] as int
1476 by {
1477 if c == best_g && d == best_g {
1478 assert(false);
1479 } else if c == best_g {
1480 lemma_priority_strict(self.scores@, extra_old, best_g, d);
1481 } else if d == best_g {
1482 assert(1 + extra_old[c] as int <= 1 + extra_old[best_g] as int);
1483 } else {
1484 assert(1 + extra_old[c] as int <= 1 + extra_old[d] as int);
1485 }
1486 }
1487 assert forall|c: int, d: int| 0 <= c < n && 0 <= d < n && self.scores@[c] == self.scores@[d]
1488 implies 1 + self.extra@[c] as int <= 1 + self.extra@[d] as int + 1
1489 by {
1490 if c == best_g && d == best_g {
1491 assert(1 + extra_old[c] as int <= 1 + extra_old[d] as int + 1);
1492 } else if c == best_g {
1493 if self.scores@[best_g] > 0 {
1494 lemma_priority_tie(self.scores@, extra_old, best_g, d);
1495 } else {
1496 assert(1 + extra_old[c] as int <= 1 + extra_old[d] as int + 1);
1497 }
1498 } else if d == best_g {
1499 assert(1 + extra_old[c] as int <= 1 + extra_old[d] as int + 1);
1500 } else {
1501 assert(1 + extra_old[c] as int <= 1 + extra_old[d] as int + 1);
1502 }
1503 }
1504 let old_sum = sum_to(extra_old, n as int);
1505 lemma_sum_ge_one(extra_old, best_g, n as int);
1506 lemma_sum_update(extra_old, best_g, (extra_old[best_g] + 1) as u64, n as int);
1507 assert(sum_to(self.extra@, n as int) == old_sum + 1);
1508 assert(sum_to(self.extra@, n as int) + n as int <= self.weight_total as int);
1509 assert forall|k: int| 0 <= k < n
1510 implies self.extra@[k] <= self.weight_total by {
1511 if k == best_g {
1512 assert(extra_old[k] <= old_sum);
1513 }
1514 }
1515 assert(self.normalization_when_terminal()) by {
1516 if self.terminal() {
1517 assert(self.normalization());
1518 }
1519 }
1520 assert(self.mutable_score_inv());
1521 }
1522 best
1523 }
1524
1525 pub fn update_score(&mut self, c: usize, v: u64)
1529 requires
1530 old(self).mutable_score_inv(),
1531 c < old(self).scores.len(),
1532 1 <= v <= old(self).max_score,
1533 ensures
1534 final(self).weight_total == old(self).weight_total,
1535 final(self).max_score == old(self).max_score,
1536 final(self).scores@
1537 == old(self).scores@.update(c as int, v),
1538 final(self).extra@.len() == old(self).extra@.len(),
1539 forall|i: int| 0 <= i < final(self).extra.len() ==> final(self).extra@[i] == 0,
1540 final(self).mutable_score_inv(),
1541 {
1542 let ghost old_scores = self.scores@;
1543 self.scores.set(c, v);
1544 let ghost updated_scores = self.scores@;
1545 let n = self.extra.len();
1546 let mut i: usize = 0;
1547 while i < n
1548 invariant
1549 i <= n,
1550 n == self.extra.len(),
1551 n == self.scores.len(),
1552 self.scores@ == updated_scores,
1553 updated_scores == old_scores.update(c as int, v),
1554 self.weight_total == old(self).weight_total,
1555 self.max_score == old(self).max_score,
1556 forall|k: int| 0 <= k < i ==> self.extra@[k] == 0,
1557 forall|k: int| i <= k < n ==> self.extra@[k] == old(self).extra@[k],
1558 decreases n - i,
1559 {
1560 self.extra.set(i, 0);
1561 i = i + 1;
1562 }
1563 proof {
1564 lemma_sum_zero(self.extra@, n as int);
1565 assert(self.score_bounds()) by {
1566 assert forall|k: int| 0 <= k < n implies
1567 self.scores@[k] >= 1 && self.scores@[k] <= 1_000_000_000 by {
1568 if k != c as int {
1569 assert(self.scores@[k] == old(self).scores@[k]);
1570 }
1571 }
1572 }
1573 assert(self.score_order_preservation()) by {
1574 assert forall|a: int, b: int| 0 <= a < n && 0 <= b < n
1575 && self.scores@[a] < self.scores@[b]
1576 implies self.weight(a) <= self.weight(b) by {}
1577 }
1578 assert(self.tie_boundedness()) by {
1579 assert forall|a: int, b: int| 0 <= a < n && 0 <= b < n
1580 && self.scores@[a] == self.scores@[b]
1581 implies self.weight(a) <= self.weight(b) + 1 by {}
1582 }
1583 assert(self.normalization_when_terminal()) by {
1584 if self.terminal() {
1585 assert(self.normalization());
1586 }
1587 }
1588 assert(self.mutable_score_inv());
1589 }
1590 }
1591}
1592
1593pub open spec fn count_true(s: Seq<bool>, n: int) -> int
1597 decreases n,
1598{
1599 if n <= 0 { 0 } else if n > s.len() as int { 0 }
1600 else { (if s[n - 1] { 1int } else { 0int }) + count_true(s, n - 1) }
1601}
1602
1603pub proof fn lemma_count_set(s: Seq<bool>, m: int, n: int)
1605 requires 0 <= m < n <= s.len(), !s[m],
1606 ensures count_true(s.update(m, true), n) == count_true(s, n) + 1,
1607 decreases n,
1608{
1609 if n == m + 1 {
1610 lemma_count_unaffected(s, m, n - 1);
1611 } else {
1612 lemma_count_set(s, m, n - 1);
1613 }
1614}
1615
1616pub proof fn lemma_count_unaffected(s: Seq<bool>, m: int, p: int)
1618 requires 0 <= p <= m < s.len(),
1619 ensures count_true(s.update(m, true), p) == count_true(s, p),
1620 decreases p,
1621{
1622 if p > 0 {
1623 lemma_count_unaffected(s, m, p - 1);
1624 }
1625}
1626
1627pub proof fn lemma_count_zero(s: Seq<bool>, n: int)
1629 requires 0 <= n <= s.len(), forall|k: int| 0 <= k < n ==> !s[k],
1630 ensures count_true(s, n) == 0,
1631 decreases n,
1632{
1633 if n > 0 {
1634 lemma_count_zero(s, n - 1);
1635 }
1636}
1637
1638pub proof fn lemma_count_all_true(s: Seq<bool>, n: int)
1640 requires 0 <= n <= s.len(), forall|k: int| 0 <= k < n ==> s[k],
1641 ensures count_true(s, n) == n,
1642 decreases n,
1643{
1644 if n > 0 {
1645 lemma_count_all_true(s, n - 1);
1646 }
1647}
1648
1649pub proof fn lemma_count_upper(s: Seq<bool>, n: int)
1651 requires 0 <= n <= s.len(),
1652 ensures count_true(s, n) <= n,
1653 decreases n,
1654{
1655 if n > 0 {
1656 lemma_count_upper(s, n - 1);
1657 }
1658}
1659
1660pub struct CompetitiveSelectionRanked {
1662 pub scores: Vec<u64>,
1664 pub selected: Vec<bool>,
1666 pub k: usize,
1668 pub max_score: u64,
1670}
1671
1672impl CompetitiveSelectionRanked {
1673 pub open spec fn type_invariant(&self) -> bool {
1675 self.selected.len() == self.scores.len()
1676 && forall|i: int| 0 <= i < self.scores.len() ==>
1677 #[trigger] self.scores@[i] <= self.max_score
1678 }
1679
1680 pub open spec fn bounded_multiplicity(&self) -> bool {
1682 count_true(self.selected@, self.selected@.len() as int) <= self.k
1683 }
1684
1685 pub open spec fn threshold_optimality(&self) -> bool {
1687 forall|s: int, c: int|
1688 #![trigger self.selected@[s], self.selected@[c]]
1689 0 <= s < self.selected.len() && 0 <= c < self.selected.len()
1690 && self.selected@[s] && !self.selected@[c]
1691 ==> self.scores@[s] >= self.scores@[c]
1692 }
1693
1694 pub open spec fn ranked_tie_break(&self) -> bool {
1699 forall|s: int, c: int|
1700 #![trigger self.selected@[s], self.selected@[c]]
1701 0 <= s < self.selected.len() && 0 <= c < self.selected.len()
1702 && self.selected@[s] && !self.selected@[c]
1703 ==> (self.scores@[s] > self.scores@[c]
1704 || (self.scores@[s] == self.scores@[c] && s < c))
1705 }
1706
1707 pub open spec fn inv(&self) -> bool {
1709 self.type_invariant() && self.bounded_multiplicity()
1710 && self.threshold_optimality() && self.ranked_tie_break()
1711 }
1712
1713 pub fn new(scores: Vec<u64>, k: usize, max_score: u64) -> (r: CompetitiveSelectionRanked)
1715 requires
1716 forall|i: int| 0 <= i < scores.len() ==> #[trigger] scores@[i] <= max_score,
1717 ensures
1718 r.scores@ == scores@,
1719 r.k == k,
1720 r.max_score == max_score,
1721 r.selected@.len() == scores@.len(),
1722 forall|j: int| 0 <= j < r.selected@.len() ==> !r.selected@[j],
1723 r.inv(),
1724 {
1725 let n = scores.len();
1726 let mut selected: Vec<bool> = Vec::new();
1727 let mut i: usize = 0;
1728 while i < n
1729 invariant
1730 i <= n,
1731 selected.len() == i,
1732 forall|j: int| 0 <= j < i ==> !selected@[j],
1733 decreases n - i,
1734 {
1735 selected.push(false);
1736 i = i + 1;
1737 }
1738 proof { lemma_count_zero(selected@, selected@.len() as int); }
1739 CompetitiveSelectionRanked { scores, selected, k, max_score }
1740 }
1741
1742 fn find_max_unselected(&self) -> (r: Option<usize>)
1744 requires self.type_invariant(),
1745 ensures
1746 r is None ==> (forall|c: int| 0 <= c < self.selected.len() ==> self.selected@[c]),
1747 r matches Option::Some(m) ==> (m < self.scores.len() && !self.selected@[m as int]
1748 && (forall|c: int| 0 <= c < self.scores.len() && !self.selected@[c]
1749 ==> #[trigger] self.scores@[c] <= self.scores@[m as int])
1750 && (forall|c: int| 0 <= c < self.scores.len() && !self.selected@[c]
1751 && #[trigger] self.scores@[c] == self.scores@[m as int]
1752 ==> m as int <= c)),
1753 {
1754 let n = self.scores.len();
1755 let mut best: Option<usize> = None;
1756 let mut i: usize = 0;
1757 while i < n
1758 invariant
1759 i <= n,
1760 n == self.scores.len(),
1761 self.selected.len() == n,
1762 best is None ==> (forall|c: int| 0 <= c < i ==> self.selected@[c]),
1763 best matches Option::Some(m) ==> (m < i && !self.selected@[m as int]
1764 && (forall|c: int| 0 <= c < i && !self.selected@[c]
1765 ==> #[trigger] self.scores@[c] <= self.scores@[m as int])
1766 && (forall|c: int| 0 <= c < i && !self.selected@[c]
1767 && #[trigger] self.scores@[c] == self.scores@[m as int]
1768 ==> m as int <= c)),
1769 decreases n - i,
1770 {
1771 if !self.selected[i] {
1772 match best {
1773 Option::Some(m) => {
1774 if self.scores[i] > self.scores[m] {
1775 best = Some(i);
1776 }
1777 }
1778 Option::None => {
1779 best = Some(i);
1780 }
1781 }
1782 }
1783 i = i + 1;
1784 }
1785 best
1786 }
1787
1788 pub fn select(&mut self)
1791 requires old(self).type_invariant(),
1792 ensures
1793 final(self).scores@ == old(self).scores@,
1794 final(self).k == old(self).k,
1795 final(self).max_score == old(self).max_score,
1796 count_true(final(self).selected@, final(self).selected@.len() as int)
1797 == if final(self).k < final(self).selected.len() {
1798 final(self).k as int
1799 } else {
1800 final(self).selected.len() as int
1801 },
1802 final(self).inv(),
1803 {
1804 let n = self.scores.len();
1805 let original_len = self.selected.len();
1806 let original_k = self.k;
1807 let _ = (original_len, original_k);
1808 assert(original_len == n);
1809 let mut i: usize = 0;
1811 while i < n
1812 invariant
1813 i <= n,
1814 n == self.scores.len(),
1815 self.selected.len() == n,
1816 self.type_invariant(),
1817 self.scores@ == old(self).scores@,
1818 self.k == old(self).k,
1819 self.max_score == old(self).max_score,
1820 original_len == n,
1821 original_k == self.k,
1822 forall|j: int| 0 <= j < i ==> !self.selected@[j],
1823 decreases n - i,
1824 {
1825 self.selected.set(i, false);
1826 i = i + 1;
1827 }
1828 proof { lemma_count_zero(self.selected@, n as int); }
1829 let mut round: usize = 0;
1831 while round < self.k
1832 invariant
1833 n == self.scores.len(),
1834 self.selected.len() == n,
1835 self.type_invariant(),
1836 self.scores@ == old(self).scores@,
1837 self.k == old(self).k,
1838 self.max_score == old(self).max_score,
1839 original_len == n,
1840 original_k == self.k,
1841 round <= self.k,
1842 count_true(self.selected@, n as int) == round,
1843 self.threshold_optimality(),
1844 self.ranked_tie_break(),
1845 decreases self.k - round,
1846 {
1847 let m_opt = self.find_max_unselected();
1848 match m_opt {
1849 Option::Some(m) => {
1850 let ghost s0 = self.selected@;
1851 assert forall|c: int| 0 <= c < n && !s0[c]
1852 implies self.scores@[c] <= self.scores@[m as int] by {}
1853 assert forall|c: int| 0 <= c < n && !s0[c]
1854 && self.scores@[c] == self.scores@[m as int]
1855 implies m as int <= c by {}
1856 proof { lemma_count_set(s0, m as int, n as int); }
1857 self.selected.set(m, true);
1858 proof {
1859 assert(self.threshold_optimality()) by {
1860 assert forall|s: int, c: int|
1861 #![trigger self.selected@[s], self.selected@[c]]
1862 0 <= s < n && 0 <= c < n && self.selected@[s] && !self.selected@[c]
1863 implies self.scores@[s] >= self.scores@[c] by {
1864 if s != m as int {
1865 assert(s0[s]);
1866 }
1867 }
1868 }
1869 assert(self.ranked_tie_break()) by {
1870 assert forall|s: int, c: int|
1871 #![trigger self.selected@[s], self.selected@[c]]
1872 0 <= s < n && 0 <= c < n
1873 && self.selected@[s] && !self.selected@[c]
1874 implies self.scores@[s] > self.scores@[c]
1875 || (self.scores@[s] == self.scores@[c] && s < c) by {
1876 if s != m as int {
1877 assert(s0[s]);
1878 assert(!s0[c]);
1879 } else {
1880 assert(!s0[c]);
1881 assert(c != m as int);
1882 if self.scores@[m as int] == self.scores@[c] {
1883 assert(m as int <= c);
1884 assert((m as int) < c);
1885 }
1886 }
1887 }
1888 }
1889 }
1890 }
1891 Option::None => {
1892 proof {
1893 lemma_count_all_true(self.selected@, n as int);
1894 assert(count_true(self.selected@, n as int) == n as int);
1895 assert(round as int == n as int);
1896 assert(round < self.k);
1897 assert(n < self.k);
1898 assert(original_len == n);
1899 assert(original_k == self.k);
1900 assert(!(original_k < original_len));
1901 }
1902 return;
1903 }
1904 }
1905 round = round + 1;
1906 }
1907 proof {
1908 lemma_count_upper(self.selected@, n as int);
1909 assert(round == self.k);
1910 assert(count_true(self.selected@, n as int) == self.k as int);
1911 assert(self.k <= n);
1912 }
1913 }
1914
1915 pub fn update_scores(&mut self, new_scores: Vec<u64>)
1917 requires
1918 old(self).type_invariant(),
1919 new_scores.len() == old(self).scores.len(),
1920 forall|i: int| 0 <= i < new_scores.len() ==>
1921 #[trigger] new_scores@[i] <= old(self).max_score,
1922 ensures
1923 final(self).scores@ == new_scores@,
1924 final(self).k == old(self).k,
1925 final(self).max_score == old(self).max_score,
1926 forall|i: int| 0 <= i < final(self).selected.len() ==>
1927 !final(self).selected@[i],
1928 final(self).inv(),
1929 {
1930 let ghost ns = new_scores@;
1931 let n = new_scores.len();
1932 self.scores = new_scores;
1933 let mut i: usize = 0;
1934 while i < n
1935 invariant
1936 i <= n,
1937 n == self.scores.len(),
1938 self.selected.len() == n,
1939 self.scores@ == ns,
1940 self.k == old(self).k,
1941 self.max_score == old(self).max_score,
1942 forall|j: int| 0 <= j < i ==> !self.selected@[j],
1943 decreases n - i,
1944 {
1945 self.selected.set(i, false);
1946 i = i + 1;
1947 }
1948 proof { lemma_count_zero(self.selected@, n as int); }
1949 }
1950}
1951
1952}