(* Since posMap is a dictionary, we need to have a sortedness notation over a dictionary  *)
Definition sortedDict (dict: list (nat * nat)) := forall i j k1 v1 k2 v2,
i < j ->
nth_error dict i = Some (k1, v1) ->
nth_error dict j = Some (k2, v2) ->
k1 = i /\ k2 = j.
Fixpoint fixedSlots (rt: PBTree (list BlockEntry)) (sz : nat) : Prop :=
match rt with
| Leaf _  idx val  => (List.length val) = sz
| Node _ idx val lc rc => (List.length val) = sz /\
(fixedSlots lc sz) /\
(fixedSlots rc sz)
end.
Definition posMapSimplInit (posMap : list(nat * nat)) (memSize : nat) := List.length(posMap) = memSize /\ (sortedDict posMap).
(* TODO: show PathORAM satisfies the invariant before an access and also show that it satisfies the invariant after an access.  *)
Fixpoint In_BlEntry (lbe : list BlockEntry) (key : nat) : Prop :=
match lbe with
| [] => False
| BV k v :: t => k = key \/ In_BlEntry t key
end.
Fixpoint In_PBTree (rt : PBTree(list BlockEntry)) (key : nat) : Prop :=
match rt with
| Leaf _ idx val => idx = key \/ False
| Node _ idx val lc rc =>
idx = key \/ In_PBTree lc key \/
In_PBTree rc key
end.
(* Print xorb. *)
(* Inductive xxor : Prop -> Prop -> Prop := *)
(* | f_f (x y : Prop): ~x -> ~y -> xxor x y *)
(* | f_t (x y : Prop): ~x -> y -> xxor x y *)
(* | t_f(x y : Prop): x -> ~y -> xxor x y *)
(* | t_t (x y : Prop): x -> y -> xxor x y. *)
(* Definition invariant (stsh: list BlockEntry) (rt: PBTree (list BlockEntry)) (memSz : nat) := *)
(*   forall i, O <= i < memSz -> xxor (In_BlEntry stsh i) (In_PBTree rt i). *)
Fixpoint counting_begin (begin : nat) (lnat : list nat) : bool :=
match lnat with
| [] => true
| h :: t => if Nat.eqb begin h
then (counting_begin (S begin) t)
else false
end.
Fixpoint count_lst (lnat : list nat) : bool :=
match lnat with
| [] => true
| h :: t => (counting_begin h lnat)
end.
Compute count_lst [109;110;111].
Compute NatSort.sort [1;3;2].
(* get the list of block ids that live in the stash or a given path *)
Definition getBlockIdsFromBELst (belst : list BlockEntry) : list nat :=
List.map BlockEntry_fst belst.
Fixpoint getBlockIdsFromPath (rt: PBTree (list BlockEntry)) (l : list nat) : list nat :=
match l with
| [] => []
| h :: t => match rt with
| Leaf _ idx val =>
if Nat.eqb h idx
then getBlockIdsFromBELst val
else getBlockIdsFromPath rt t
| Node _ idx val lc rc =>
if Nat.eqb h idx
then (getBlockIdsFromBELst val) ++ (getBlockIdsFromPath lc t) ++
(getBlockIdsFromPath rc t)
else (getBlockIdsFromPath lc t) ++ (getBlockIdsFromPath rc t)
end
end.
Definition caterpillar (BId_stsh : list nat) (BId_path : list nat) : Prop :=
NoDup (NatSort.sort (BId_stsh ++ BId_path)).
Definition isLeafNode (n : nat) (rt : PBTree (list BlockEntry)) : Prop :=
let l := (getHeight rt) in
(ge n (2^(l - 1))) /\ (le n (2^l - 1)).
Definition init_invariant
(s: st_rand) (memSz : nat): Prop :=
match s with
| (strm, (stsh, rt)) =>
forall bId posMap,
bId < memSz ->
(exists x, (isLeafNode x rt ->
In bId (getBlockIdsFromPath rt (getPath' x)) \/
(In bId (getBlockIdsFromBELst stsh))) /\
(posMapLookUp bId posMap = (Some x)))
end.

(* Specifications: *)
Lemma access_rec_simpl: forall leafIdx lIDs lvl,
(access_rec leafIdx lIDs (S lvl)) =
let* _ := writeBacks leafIdx lIDs (S lvl)
in access_rec leafIdx lIDs lvl.
Lemma runStateLemma: forall s (arSt wbSt: state st_rand unit),
runState (let* _ := wbSt in arSt) s =
let (u', s') := runState wbSt s in runState arSt s'.