datatype List<T> = Nil | Cons(head:T,tail:List<T>)
datatype Option<T> = None | Some(elem:T)
ghost function mem<T>(x:T,l:List<T>) : bool {
case Cons(y,xs) => x==y || mem(x,xs)
ghost function length<T>(l:List<T>) : int {
case Cons(_,xs) => 1 + length(xs)
function list_find<K(==),V(!new)>(k:K,l:List<(K,V)>) : Option<V>
ensures match list_find(k,l) {
case None => forall v :: !mem((k,v),l)
case Some(v) => mem((k,v),l)
case Cons((k',v),xs) => if k==k' then Some(v) else list_find(k,xs)
function list_remove<K(==,!new),V(!new)>(k:K, l:List<(K,V)>) : List<(K,V)>
ensures forall k',v :: mem((k',v),list_remove(k,l)) <==> (mem((k',v),l) && k != k')
case Cons((k',v),xs) => if k==k' then list_remove(k,xs) else
Cons((k',v),list_remove(k,xs))
class Hashtable<K(==,!new),V(!new)> {
var data : array<List<(K,V)>>
ghost var Repr : set<object>
ghost var elems : map<K,Option<V>>
this in Repr && data in Repr && data.Length > 0 &&
(forall i :: 0 <= i < data.Length ==> valid_hash(data, i)) &&
(forall k,v :: valid_data(k,v,elems,data))
ghost predicate valid_hash(data: array<List<(K,V)>>, i: int)
requires 0 <= i < data.Length
forall k,v :: mem((k,v), data[i]) ==> (bucket(k,data.Length) == i)
ghost predicate valid_data(k: K,v: V,elems: map<K, Option<V>>, data: array<List<(K,V)>>)
(k in elems && elems[k] == Some(v)) <==> mem((k,v), data[bucket(k, data.Length)])
function hash(key:K) : int
function bucket(k: K, n: int) : int
ensures 0 <= bucket(k, n) < n
ensures fresh(Repr-{this})
data := new List<(K,V)>[n](i => Nil);
ensures fresh(Repr - old(Repr))
invariant 0 <= i <= data.Length
invariant forall j :: 0 <= j < i ==> data[j] == Nil
ensures fresh(Repr - old(Repr))
ensures forall key :: key in old(elems) ==> key in elems
ensures forall k,v :: k in old(elems) && old(elems)[k] == Some(v) ==> k in elems && elems[k] == Some(v)
var newData := new List<(K,V)>[data.Length * 2](i => Nil);
var oldSize := data.Length;
var newSize := newData.Length;
assert forall i :: 0 <= i < data.Length ==> valid_hash(data,i);
invariant 0 <= i <= data.Length
invariant newData != data
invariant old(data) == data
invariant old(size) == size
invariant Repr == old(Repr)
invariant 0 < oldSize == data.Length
invariant data.Length*2 == newData.Length == newSize
invariant forall j :: 0 <= j < newSize ==> valid_hash(newData, j)
invariant forall k,v :: (
if 0<= bucket(k, oldSize) < i then
valid_data(k,v,elems,newData)
!mem((k,v), newData[bucket(k, newSize)]))
assert valid_hash(data,i);
if 0 <= bucket(k, oldSize) < i then
valid_data(k,v,elems,data)
else if bucket(k, oldSize) == i then
((k in elems && elems[k] == Some(v))
<==> mem((k,v), data[bucket(k,data.Length)]) || mem((k,v), newData[bucket(k, newSize)]))
!mem((k,v), newData[bucket(k, newSize)]));
rehash(data[i],newData,i,oldSize,newSize);
Repr := Repr - {data} + {newData};
method rehash(l: List<(K,V)>, newData: array<List<(K,V)>>,i: int, oldSize: int, newSize: int)
requires 0 < oldSize == data.Length
requires newData.Length == 2 * oldSize == newSize
requires forall k,v :: mem((k,v), l) ==> bucket(k, oldSize) == i
requires forall j :: 0 <= j < newSize ==> valid_hash(newData, j)
if 0 <= bucket(k, oldSize) < i then
valid_data(k,v,elems,newData)
else if bucket(k, oldSize) == i then
((k in elems && elems[k] == Some(v))
<==> mem((k,v), l) || mem((k,v),newData[bucket(k, newSize)]))
!mem((k,v),newData[bucket(k, newSize)]))
ensures forall j :: 0 <= j < newSize ==> valid_hash(newData, j)
(if 0 <= bucket(k, oldSize) <= i then
valid_data(k,v,elems,newData)
!mem((k,v),newData[bucket(k, newSize)]))
var b := bucket(k, newSize);
newData[b] := Cons((k,v), newData[b]);
rehash(r, newData, i, oldSize, newSize);
method find(k: K) returns (r: Option<V>)
case None => (k !in elems || (k in elems && elems[k] == None))
case Some(v) => (k in elems && elems[k] == Some(v))
assert forall k, v :: valid_data(k,v,elems,data) && ((k in elems && elems[k] == Some(v)) <==> (mem((k,v),data[bucket(k,data.Length)])));
var idx := bucket(k, data.Length);
r := list_find(k, data[idx]);
assert match list_find(k,data[bucket(k, data.Length)])
case None => forall v :: idx == bucket(k,data.Length) && !mem((k,v),data[idx])
case Some(v) => mem((k,v),data[bucket(k,data.Length)]);
ensures fresh(Repr - old(Repr))
ensures k !in elems || elems[k] == None
ensures forall key :: key != k && key in old(elems) ==> key in elems && elems[key] == old(elems[key])
assert forall i :: 0 <= i < data.Length ==> valid_hash(data, i);
assert forall k,v :: valid_data(k,v,elems,data);
var idx := bucket(k, data.Length);
var opt := list_find(k, data[idx]);
assert forall i :: 0 <= i < data.Length ==> valid_hash(data,i) && (forall k,v:: mem((k,v), data[i]) ==> (bucket(k,data.Length) == i));
assert forall k,v :: valid_data(k,v,elems, data) && ((k in elems && elems[k] == Some(v)) <==> (mem((k,v), data[bucket(k, data.Length)])));
assert forall i :: 0 <= i < data.Length ==> valid_hash(data,i);
assert forall v :: !mem((k,v),data[bucket(k,data.Length)]);
assert forall k,v :: valid_data(k,v,elems,data) && ((k in elems && elems[k] == Some(v)) <==> (mem((k,v),data[bucket(k,data.Length)])));
var idx := bucket(k, data.Length);
data[idx] := list_remove(k, data[idx]);
elems := elems[k := None];
ensures fresh(Repr - old(Repr))
ensures k in elems && elems[k] == Some(v)
ensures forall key :: key != k && key in old(elems) ==> key in elems
if(size >= data.Length * 3/4) {
assert forall i :: 0 <= i < data.Length ==> valid_hash(data, i);
var ind := bucket(k,data.Length);
assert forall i :: 0 <= i < data.Length ==> valid_hash(data, i) && (forall k,v:: mem((k,v), data[i]) ==> (bucket(k,data.Length) == i));
assert forall k,v :: valid_data(k,v,elems, data) && ((k in elems && elems[k] == Some(v)) <==> (mem((k,v), data[bucket(k, data.Length)])));
assert forall k,v :: mem((k,v), data[ind]) ==> (bucket(k,data.Length) == ind);
data[ind] := Cons((k,v), data[ind]);
elems := elems[k := Some(v)];
assert bucket(k,data.Length) == ind;
assert mem((k,v), data[bucket(k,data.Length)]);
assert k in elems && elems[k] == Some(v);