use vstd::prelude::*;
verus! {
pub open spec fn has_key(entries: Seq<(u64, u64)>, n: int, k: u64) -> bool {
exists|i: int| 0 <= i < n && entries[i].0 == k
}
pub proof fn lemma_has_key_extend(entries: Seq<(u64, u64)>, n: int, k: u64)
requires 0 <= n < entries.len(),
ensures
has_key(entries, n + 1, k) == (has_key(entries, n, k) || entries[n].0 == k),
{
if has_key(entries, n + 1, k) {
let i = choose|i: int| 0 <= i < n + 1 && entries[i].0 == k;
assert(i < n || i == n);
}
if has_key(entries, n, k) {
let i = choose|i: int| 0 <= i < n && entries[i].0 == k;
assert(0 <= i < n + 1 && entries[i].0 == k);
}
if entries[n].0 == k {
assert(0 <= n < n + 1 && entries[n].0 == k);
}
}
pub proof fn lemma_push_has_key(entries: Seq<(u64, u64)>, a: u64, b: u64, kk: u64)
ensures
has_key(entries.push((a, b)), entries.len() as int + 1, kk)
== (has_key(entries, entries.len() as int, kk) || kk == a),
{
let pushed = entries.push((a, b));
if has_key(entries, entries.len() as int, kk) {
let i = choose|i: int| 0 <= i < entries.len() && entries[i].0 == kk;
assert(pushed[i] == entries[i]);
}
if kk == a {
assert(pushed[entries.len() as int].0 == kk);
}
if has_key(pushed, entries.len() as int + 1, kk) {
let i = choose|i: int| 0 <= i < entries.len() as int + 1 && pushed[i].0 == kk;
if i < entries.len() {
assert(entries[i] == pushed[i]);
}
}
}
pub open spec fn has_pair(entries: Seq<(u64, u64)>, n: int, k: u64, v: u64) -> bool {
exists|i: int| 0 <= i < n && entries[i].0 == k && entries[i].1 == v
}
pub open spec fn unique_mapping_entries(entries: Seq<(u64, u64)>) -> bool {
forall|i: int, j: int|
(0 <= i < entries.len() && 0 <= j < entries.len() && i != j)
==> #[trigger] entries[i].0 != #[trigger] entries[j].0
}
pub open spec fn unique_keys<K>(keys: Seq<K>) -> bool {
forall|i: int, j: int|
(0 <= i < keys.len() && 0 <= j < keys.len() && i != j)
==> #[trigger] keys[i] != #[trigger] keys[j]
}
pub open spec fn unique_present_keys<K>(keys: Seq<K>, empty: K) -> bool {
forall|i: int, j: int|
(0 <= i < keys.len()
&& 0 <= j < keys.len()
&& i != j
&& #[trigger] keys[i] != empty
&& #[trigger] keys[j] != empty)
==> keys[i] != keys[j]
}
pub open spec fn contains_key_value<K>(keys: Seq<K>, key: K) -> bool {
exists|i: int| 0 <= i < keys.len() && #[trigger] keys[i] == key
}
pub proof fn contains_key_value_push<K>(keys: Seq<K>, added: K, key: K)
ensures
contains_key_value(keys.push(added), key)
== (contains_key_value(keys, key) || key == added),
{
let pushed = keys.push(added);
if contains_key_value(keys, key) {
let index = choose|index: int| 0 <= index < keys.len() && keys[index] == key;
assert(pushed[index] == keys[index]);
}
if key == added {
assert(pushed[keys.len() as int] == key);
}
if contains_key_value(pushed, key) {
let index = choose|index: int| 0 <= index < pushed.len() && pushed[index] == key;
if index < keys.len() {
assert(keys[index] == pushed[index]);
} else {
assert(index == keys.len());
}
}
}
pub proof fn contains_key_value_remove_unique<K>(keys: Seq<K>, removed: int, key: K)
requires
unique_keys(keys),
0 <= removed < keys.len(),
ensures
contains_key_value(keys.remove(removed), key)
== (contains_key_value(keys, key) && key != keys[removed]),
{
keys.remove_ensures(removed);
let reduced = keys.remove(removed);
if contains_key_value(reduced, key) {
let index = choose|index: int| 0 <= index < reduced.len() && reduced[index] == key;
let old_index = if index < removed { index } else { index + 1 };
assert(0 <= old_index < keys.len());
assert(old_index != removed);
assert(reduced[index] == keys[old_index]);
assert(contains_key_value(keys, key));
if key == keys[removed] {
assert(keys[old_index] == keys[removed]);
assert(false);
}
}
if contains_key_value(keys, key) && key != keys[removed] {
let old_index = choose|index: int| 0 <= index < keys.len() && keys[index] == key;
assert(old_index != removed);
let index = if old_index < removed { old_index } else { old_index - 1 };
assert(0 <= index < reduced.len());
assert(reduced[index] == keys[old_index]);
}
}
pub proof fn lemma_has_pair_extend(entries: Seq<(u64, u64)>, n: int, k: u64, v: u64)
requires 0 <= n < entries.len(),
ensures
has_pair(entries, n + 1, k, v)
== (has_pair(entries, n, k, v) || (entries[n].0 == k && entries[n].1 == v)),
{
if has_pair(entries, n + 1, k, v) {
let i = choose|i: int| 0 <= i < n + 1 && entries[i].0 == k && entries[i].1 == v;
assert(i < n || i == n);
}
if has_pair(entries, n, k, v) {
let i = choose|i: int| 0 <= i < n && entries[i].0 == k && entries[i].1 == v;
assert(0 <= i < n + 1 && entries[i].0 == k && entries[i].1 == v);
}
if entries[n].0 == k && entries[n].1 == v {
assert(0 <= n < n + 1 && entries[n].0 == k && entries[n].1 == v);
}
}
pub proof fn lemma_push_has_pair(entries: Seq<(u64, u64)>, a: u64, b: u64, kk: u64, vv: u64)
ensures
has_pair(entries.push((a, b)), entries.len() as int + 1, kk, vv)
== (has_pair(entries, entries.len() as int, kk, vv) || (kk == a && vv == b)),
{
let pushed = entries.push((a, b));
if has_pair(entries, entries.len() as int, kk, vv) {
let i = choose|i: int|
0 <= i < entries.len() && entries[i].0 == kk && entries[i].1 == vv;
assert(pushed[i] == entries[i]);
}
if kk == a && vv == b {
assert(pushed[entries.len() as int].0 == kk && pushed[entries.len() as int].1 == vv);
}
if has_pair(pushed, entries.len() as int + 1, kk, vv) {
let i = choose|i: int|
0 <= i < entries.len() as int + 1 && pushed[i].0 == kk && pushed[i].1 == vv;
if i < entries.len() {
assert(entries[i] == pushed[i]);
}
}
}
pub struct ResourceRegistry {
pub entries: Vec<(u64, u64)>,
}
impl ResourceRegistry {
pub open spec fn unique_mapping(&self) -> bool {
unique_mapping_entries(self.entries@)
}
pub open spec fn contains_key(&self, k: u64) -> bool {
has_key(self.entries@, self.entries@.len() as int, k)
}
pub open spec fn maps_to(&self, k: u64, v: u64) -> bool {
has_pair(self.entries@, self.entries@.len() as int, k, v)
}
pub proof fn unique_value(&self, k: u64, left: u64, right: u64)
requires
self.unique_mapping(),
self.maps_to(k, left),
self.maps_to(k, right),
ensures left == right,
{
let left_index = choose|index: int|
0 <= index < self.entries@.len()
&& self.entries@[index].0 == k
&& self.entries@[index].1 == left;
let right_index = choose|index: int|
0 <= index < self.entries@.len()
&& self.entries@[index].0 == k
&& self.entries@[index].1 == right;
if left_index != right_index {
assert(self.entries@[left_index].0 != self.entries@[right_index].0);
}
assert(left_index == right_index);
}
pub proof fn maps_to_implies_contains(&self, k: u64, v: u64)
requires self.maps_to(k, v),
ensures self.contains_key(k),
{
let index = choose|index: int|
0 <= index < self.entries@.len()
&& self.entries@[index].0 == k
&& self.entries@[index].1 == v;
assert(0 <= index < self.entries@.len() && self.entries@[index].0 == k);
}
pub fn new() -> (r: ResourceRegistry)
ensures
r.entries@.len() == 0,
r.unique_mapping(),
{
ResourceRegistry { entries: Vec::new() }
}
fn without_key(entries: &Vec<(u64, u64)>, k: u64) -> (out: Vec<(u64, u64)>)
requires
forall|i: int, j: int|
(0 <= i < entries@.len() && 0 <= j < entries@.len() && i != j)
==> #[trigger] entries@[i].0 != #[trigger] entries@[j].0,
ensures
forall|i: int| 0 <= i < out@.len() ==> out@[i].0 != k,
forall|i: int, j: int|
(0 <= i < out@.len() && 0 <= j < out@.len() && i != j)
==> #[trigger] out@[i].0 != #[trigger] out@[j].0,
forall|kk: u64|
kk != k ==>
(#[trigger] has_key(out@, out@.len() as int, kk)
== has_key(entries@, entries@.len() as int, kk)),
forall|kk: u64, vv: u64|
kk != k ==>
(#[trigger] has_pair(out@, out@.len() as int, kk, vv)
== has_pair(entries@, entries@.len() as int, kk, vv)),
{
let mut out: Vec<(u64, u64)> = Vec::new();
let mut i: usize = 0;
while i < entries.len()
invariant
i <= entries.len(),
forall|a: int, b: int|
(0 <= a < entries@.len() && 0 <= b < entries@.len() && a != b)
==> #[trigger] entries@[a].0 != #[trigger] entries@[b].0,
forall|a: int| 0 <= a < out@.len() ==> out@[a].0 != k,
forall|a: int, b: int|
(0 <= a < out@.len() && 0 <= b < out@.len() && a != b)
==> #[trigger] out@[a].0 != #[trigger] out@[b].0,
forall|kk: u64|
kk != k ==>
(#[trigger] has_key(out@, out@.len() as int, kk)
== has_key(entries@, i as int, kk)),
forall|kk: u64, vv: u64|
kk != k ==>
(#[trigger] has_pair(out@, out@.len() as int, kk, vv)
== has_pair(entries@, i as int, kk, vv)),
decreases entries.len() - i,
{
let e = entries[i];
let ghost ob = out@;
if e.0 != k {
assert(!has_key(entries@, i as int, e.0)) by {
if has_key(entries@, i as int, e.0) {
let t = choose|t: int| 0 <= t < i as int && entries@[t].0 == e.0;
assert(entries@[t].0 != entries@[i as int].0); }
}
assert(!has_key(ob, ob.len() as int, e.0)); out.push(e);
assert forall|a: int, b: int|
(0 <= a < out@.len() && 0 <= b < out@.len() && a != b)
implies #[trigger] out@[a].0 != #[trigger] out@[b].0 by {
if a < ob.len() && b < ob.len() {
} else if b == ob.len() && a < ob.len() {
assert(out@[a] == ob[a]);
assert(has_key(ob, ob.len() as int, ob[a].0));
} else if a == ob.len() && b < ob.len() {
assert(out@[b] == ob[b]);
assert(has_key(ob, ob.len() as int, ob[b].0));
}
}
}
assert forall|kk: u64| kk != k implies
(#[trigger] has_key(out@, out@.len() as int, kk)
== has_key(entries@, (i + 1) as int, kk)) by {
lemma_has_key_extend(entries@, i as int, kk);
if e.0 != k {
lemma_push_has_key(ob, e.0, e.1, kk);
}
}
assert forall|kk: u64, vv: u64| kk != k implies
(#[trigger] has_pair(out@, out@.len() as int, kk, vv)
== has_pair(entries@, (i + 1) as int, kk, vv)) by {
lemma_has_pair_extend(entries@, i as int, kk, vv);
if e.0 != k {
lemma_push_has_pair(ob, e.0, e.1, kk, vv);
}
}
i = i + 1;
}
out
}
pub fn lookup(&self, k: u64) -> (res: Option<u64>)
requires self.unique_mapping(),
ensures
res matches Option::Some(v) ==> self.maps_to(k, v),
res is None ==> !self.contains_key(k),
{
let len = self.entries.len();
let mut i: usize = 0;
while i < len
invariant
i <= len,
len == self.entries.len(),
forall|t: int| 0 <= t < i ==> self.entries@[t].0 != k,
decreases len - i,
{
if self.entries[i].0 == k {
assert(self.entries@[i as int].0 == k && self.entries@[i as int].1 == self.entries@[i as int].1);
return Some(self.entries[i].1);
}
i = i + 1;
}
assert(!self.contains_key(k));
None
}
pub fn register(&mut self, k: u64, v: u64)
requires old(self).unique_mapping(),
ensures
final(self).unique_mapping(),
final(self).maps_to(k, v),
forall|kk: u64|
kk != k ==>
(#[trigger] final(self).contains_key(kk) == old(self).contains_key(kk)),
forall|kk: u64, vv: u64|
kk != k ==>
(#[trigger] final(self).maps_to(kk, vv) == old(self).maps_to(kk, vv)),
{
let filtered = Self::without_key(&self.entries, k);
let ghost fb = filtered@;
self.entries = filtered;
self.entries.push((k, v));
assert forall|i: int, j: int|
(0 <= i < self.entries@.len() && 0 <= j < self.entries@.len() && i != j)
implies #[trigger] self.entries@[i].0 != #[trigger] self.entries@[j].0 by {
if i < fb.len() && j < fb.len() {
} else if j == fb.len() && i < fb.len() {
assert(self.entries@[i] == fb[i]);
assert(fb[i].0 != k); } else if i == fb.len() && j < fb.len() {
assert(self.entries@[j] == fb[j]);
assert(fb[j].0 != k);
}
}
assert(self.maps_to(k, v)) by {
assert(self.entries@[fb.len() as int].0 == k && self.entries@[fb.len() as int].1 == v);
}
assert forall|kk: u64| kk != k implies
(#[trigger] self.contains_key(kk) == old(self).contains_key(kk)) by {
lemma_push_has_key(fb, k, v, kk);
}
assert forall|kk: u64, vv: u64| kk != k implies
(#[trigger] self.maps_to(kk, vv) == old(self).maps_to(kk, vv)) by {
lemma_push_has_pair(fb, k, v, kk, vv);
}
}
pub fn deregister(&mut self, k: u64)
requires
old(self).unique_mapping(),
old(self).contains_key(k),
ensures
final(self).unique_mapping(),
!final(self).contains_key(k),
forall|kk: u64|
kk != k ==>
(#[trigger] final(self).contains_key(kk) == old(self).contains_key(kk)),
forall|kk: u64, vv: u64|
kk != k ==>
(#[trigger] final(self).maps_to(kk, vv) == old(self).maps_to(kk, vv)),
{
let filtered = Self::without_key(&self.entries, k);
let ghost fb = filtered@;
self.entries = filtered;
assert forall|kk: u64, vv: u64| kk != k implies
(#[trigger] self.maps_to(kk, vv) == old(self).maps_to(kk, vv)) by {
assert(has_pair(fb, fb.len() as int, kk, vv)
== has_pair(old(self).entries@, old(self).entries@.len() as int, kk, vv));
}
assert(!self.contains_key(k)) by {
if self.contains_key(k) {
let i = choose|i: int| 0 <= i < self.entries@.len() && self.entries@[i].0 == k;
assert(self.entries@[i].0 != k);
}
}
}
}
}