(* Abstract properties of hashtables *)
Class hash_fun := { size : Z; hash : Z -> Z; size_pos : size > 0; hash_range : forall i, 0 <= hash i < size }.
Section Hashtable.
Context {hf : hash_fun}.
Hint Resolve size_pos hash_range.
Definition rebase {A} (m : list A) i := rotate m (Zlength m - i) (Zlength m).
Definition well_chained (m : list (Z * Z)) := forall k i, index_of (rebase m (hash k)) k = Some i ->
Forall (fun x => fst x <> 0) (sublist 0 i (rebase m (hash k))).
Definition wf_map (m : list (Z * Z)) := NoDup (map fst m).
Definition indices i j := (map (fun x => (i + x) mod size) (upto (Z.to_nat ((j - i) mod size)))).
Fixpoint index_of' (m : list (Z * Z)) k :=
match m with
| [] => None
| (k1, v1) :: rest => if eq_dec k1 0 then Some 0 else
if eq_dec k1 k then Some 0
else option_map Z.succ (index_of' rest k)
end.
Definition lookup (m : list (Z * Z)) (k : Z) :=
option_map (fun i => (i + hash k) mod size) (index_of' (rebase m (hash k)) k).
Definition set m k v := option_map (fun i => upd_Znth i m (k, v)) (lookup m k).
Definition get m k := match lookup m k with Some i => let '(k', v') := Znth i m in
if eq_dec k' 0 then None else Some v' | None => None end.

(* Specifications: *)
Lemma rebase_0 : forall {A} {d : Inhabitant A} (m : list A) i, 0 <= i < Zlength m -> Znth 0 (rebase m i) = Znth i m.
Lemma index_of'_spec : forall k m, match index_of' m k with
| Some i => 0 <= i < Zlength m /\ (fst (Znth i m) = k \/ fst (Znth i m) = 0) /\
Forall (fun x => fst x <> 0 /\ fst x <> k) (sublist 0 i m)
| None => ~In k (map fst m) /\ ~In 0 (map fst m)
end.
Lemma Znth_rebase : forall {A} {d : Inhabitant A} (m : list A) i j, 0 <= i < Zlength m -> 0 <= j < Zlength m ->
Znth i (rebase m j) = Znth ((i + j) mod Zlength m) m.
Corollary Znth_rebase' : forall {A} {d : Inhabitant A} (m : list A) i j, 0 <= i < Zlength m -> 0 <= j < Zlength m ->
Znth ((i - j) mod Zlength m) (rebase m j) = Znth i m.
Lemma index_of'_upd : forall m k v i (Hrange : 0 <= i < Zlength m)
(Hi : fst (Znth i m) = k \/ fst (Znth i m) = 0),
index_of' (upd_Znth i m (k, v)) k = index_of' m k.
Lemma Zlength_rebase : forall {A} (m : list A) i, 0 <= i < Zlength m -> Zlength (rebase m i) = Zlength m.
Lemma rebase_upd : forall {A} (m : list A) i j x, 0 <= i < Zlength m -> 0 <= j < Zlength m ->
rebase (upd_Znth i m x) j = upd_Znth ((i - j) mod Zlength m) (rebase m j) x.
Corollary rebase_upd' : forall {A} (m : list A) i j x, 0 <= i < Zlength m -> 0 <= j < Zlength m ->
rebase (upd_Znth ((i + j) mod Zlength m) m x) j = upd_Znth i (rebase m j) x.
Lemma lookup_set : forall m k v m', set m k v = Some m' -> Zlength m = size -> lookup m' k = lookup m k.
Lemma lookup_range : forall m k i, lookup m k = Some i -> Zlength m = size -> 0 <= i < Zlength m.
Lemma index_of'_upd2 : forall m k i k' v' (Hrange : 0 <= i < Zlength m)
(Hi : index_of' m k <> Some i) (Hdiff : k' <> k /\ k' <> 0),
index_of' (upd_Znth i m (k', v')) k = index_of' m k.
Lemma lookup_upd_same : forall m k i v', lookup m k = Some i -> Zlength m = size -> 0 <= i < Zlength m ->
lookup (upd_Znth i m (k, v')) k = lookup m k.
Lemma lookup_upd_diff : forall m k i k' v', lookup m k <> Some i -> Zlength m = size -> 0 <= i < Zlength m ->
k' <> k /\ k' <> 0 ->
lookup (upd_Znth i m (k', v')) k = lookup m k.
Lemma Zlength_set : forall m k v m', set m k v = Some m' -> Zlength m = size ->
Zlength m' = size.
Lemma get_set_same : forall m k v m', set m k v = Some m' -> Zlength m = size -> k <> 0 ->
get m' k = Some v.
Lemma get_override : forall m k v m' v', set m k v = Some m' -> Zlength m = size -> set m' k v' = set m k v'.
Lemma index_of_app : forall k m1 m2, index_of (m1 ++ m2) k =
match index_of m1 k with Some i => Some i | None => option_map (Z.add (Zlength m1)) (index_of m2 k) end.
Lemma index_of_out : forall k m, Forall (fun x => fst x <> k) m -> index_of m k = None.
Lemma index_of_sublist : forall m a b i k (HNoDup : NoDup (map fst m))
(Hi : index_of (sublist a b m) k = Some i) (Ha : 0 <= a) (Hb : b <= Zlength m),
index_of m k = Some (i + a).
Lemma index_of_rotate : forall m n k, 0 <= n <= Zlength m -> NoDup (map fst m) ->
index_of (rotate m n (Zlength m)) k = option_map (fun i => (i + n) mod Zlength m) (index_of m k).
Corollary index_of_rebase : forall m n k, 0 <= n <= Zlength m -> NoDup (map fst m) ->
index_of (rebase m n) k = option_map (fun i => (i - n) mod Zlength m) (index_of m k).
Lemma index_of'_succeeds : forall k m i (Hi : 0 <= i < Zlength m)
(Hnz : Forall (fun x => fst x <> 0 /\ fst x <> k) (sublist 0 i m))
(Hk : fst (Znth i m) = k \/ fst (Znth i m) = 0), index_of' m k = Some i.
Lemma lookup_spec : forall m k (Hwf : wf_map m) (Hchain : well_chained m) (Hlen : Zlength m = size)
(Hnz : k <> 0) (Hin : In k (map fst m)), lookup m k = index_of m k.