(* 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.

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.