(* What can a thread know? At least certain keys exist, and whatever it did last took effect. It can even rely on the indices of known keys. *)
Definition set_item_spec :=
DECLARE _set_item
WITH key : Z, value : Z, p : val, sh : share, entries : list val, h : list (hist * hist), l : Z
PRE [ _key OF tint, _value OF tint ]
PROP (repable_signed key; repable_signed value; readable_share sh; key <> 0; Forall isptr entries;
Zlength h = 20; wf_hists h l)
LOCAL (temp _key (vint key); temp _value (vint value); gvar _m_entries p)
SEP (data_at sh (tarray (tptr tentry) 20) entries p;
fold_right_sepcon (map (atomic_entry sh) entries);
entry_hists entries h)
POST [ tvoid ]
EX i : Z, EX h' : list (hist * hist),
PROP (set_item_trace h key value i h')
LOCAL ()
SEP (data_at sh (tarray (tptr tentry) 20) entries p;
fold_right_sepcon (map (atomic_entry sh) entries);
entry_hists entries h').
(* set_item_trace_map describes the properties on the resulting map. *)
Definition failed_load k (a b : hist * hist) := exists t r, Forall (fun x => fst x < t)%nat (fst a) /\
fst b = fst a ++ [(t, Load (Vint r))] /\ r <> Int.zero /\ r <> Int.repr k /\ snd b = snd a /\
(let v := value_of_hist (fst a) in v <> vint 0 -> v = Vint r).
(* get_item can return 0 in two cases: if the key is not in the map, or if its value is 0. In correct use, the latter should only occur if the value has not been initialized. Conceptually, this is still linearizable because we could have just checked before the key was added, but at a finer-grained level we can tell the difference from the history, so we might as well keep this information. *)
Definition get_item_trace (h : list (hist * hist)) k v i h' := 0 <= i < Zlength h /\
Forall2 (failed_load k) (sublist 0 i h) (sublist 0 i h') /\
(let '(hk, hv) := Znth i h ([], []) in exists t r, Forall (fun x => fst x < t)%nat hk /\
fst (Znth i h' ([], [])) = hk ++ [(t, Load (vint r))] /\
(v = 0 /\ r = 0 /\ snd (Znth i h' ([], [])) = hv \/
r = k /\ exists tv, Forall (fun x => fst x < tv)%nat hv /\
snd (Znth i h' ([], [])) = hv ++ [(tv, Load (vint v))]) /\
(let v := value_of_hist hk in v <> vint 0 -> v = vint r)) /\
sublist (i + 1) (Zlength h) h = sublist (i + 1) (Zlength h') h'.

(* Specifications: *)
Lemma index_of_iff_out : forall m k, index_of m k = None <-> ~In k (map fst m).
Corollary get_fail_iff : forall m k, get m k = None <-> ~In k (map fst m).
Lemma Znth_make_map : forall d h i (Hi : 0 <= i < Zlength h)
(Hnz : Forall (fun x => value_of_hist (fst x) <> vint 0) (sublist 0 (i + 1) h))
(Hint : Forall (fun x => Forall int_op (map snd (fst x))) (sublist 0 (i + 1) h)),
Znth i (make_map h) d = (make_int (value_of_hist (fst (Znth i h ([], [])))),
make_int (value_of_hist (snd (Znth i h ([], []))))).
Lemma get_item_trace_map : forall h k v i h' l (Hwf : wf_hists h l) (Htrace : get_item_trace h k v i h')
(Hk : k <> 0) (Hrepk : repable_signed k) (Hrepv : repable_signed v),
match get (make_map h') k with
| Some v' => v' = v /\ wf_hists h' (Z.max (i + 1) l) /\ incl (set (make_map h) k v) (make_map h')
| None => l <= i /\ wf_hists h' i /\ v = 0 /\ incl (make_map h) (make_map h') end.