(* Use of this source code is governed by the license described	* * in the LICENSE file at the root of the source tree.		*)
(* Operations that fold and map a computation over a list, and related theory. *)
Set Implicit Arguments.
Local Open Scope list_scope.
(* Definitions for fold, map etc.   These should probably go somewhere else. *)
(* Fold a computation over a list *)
Fixpoint compFold(A B : Set)(eqd : EqDec B)(f : B -> A -> Comp B)(init : B)(ls : list A) :=
match ls with
| nil => ret init
| a :: ls' =>
init' <-$ f init a;
compFold  _ f init' ls'
end.
(* foldBody_option is an adapter that allows you to fold a computation with signature B -> A -> Comp (option B) over a list of A, accumulating an option B. *)
Definition foldBody_option(A B : Set)(eqd : EqDec B)(f : B -> A -> Comp (option B))(b_opt : option B)(a : A) :=
match b_opt with
| None => ret None
| Some b =>
f b a
end.
Definition opt_pred(A B : Set)(P : A -> B -> Prop)(opt_a : option A)(opt_b : option B) :=
match opt_a with
| Some a =>
match opt_b with
| Some b => P a b
| None => False
end
| None =>
match opt_b with
| Some _ => False
| None => True
end
end.
intuition.
unfold foldBody_option.
unfold opt_pred in *.
destruct opt_a; destruct opt_b; intuition.
eapply comp_spec_ret; intuition.
Qed.
induction 1; intuition; simpl.
eapply comp_spec_ret; intuition.
eapply comp_spec_seq; eauto.
eapply foldBody_option_spec; eauto.
Qed.
(* Map a computation over a list. *)
Fixpoint compMap (A B : Set)(eqdb : EqDec B)(c : A -> Comp B)(ls : list A) : Comp (list B) :=
match ls with
| nil => ret nil
| a :: lsa' =>
b <-$ c a;
lsb' <-$ compMap _ c lsa';
ret (b :: lsb')
end.
intuition.
simpl.
intuition.
Qed.
intuition.
Qed.
intuition.
apply nil.
Qed.
induction ls; intuition.
unfold compMap.
prog_simp.
intuition.
simpl.
prog_inline_first.
Hint Resolve list_inhabited : inhabited.
eapply comp_spec_seq; intuition;
eauto with inhabited.
eapply comp_spec_eq_trans.
2:{
eapply comp_spec_seq_eq; eauto with inhabited.
eapply comp_spec_eq_refl.
intuition.
eapply comp_spec_seq_eq; eauto with inhabited.
intuition.
eapply comp_spec_eq_refl.
}
prog_swap_r.
prog_inline_first.
prog_skip.
Qed.
intuition.
eapply compMap_fission_eq; intuition.
simpl.
eapply comp_spec_eq_refl.
simpl.
prog_simp.
eapply comp_spec_ret.
congruence.
Qed.
induction ls; intuition.
unfold compMap.
prog_simp.
eapply comp_spec_eq_refl.
simpl.
prog_inline_first.
prog_at prog_inline rightc 1%nat.
simpl.
prog_at prog_ret rightc 2.
simpl.
prog_at prog_swap rightc 1%nat.
eapply comp_spec_eq_trans.
eapply comp_spec_seq_eq; intuition.
eapply comp_spec_eq_refl.
prog_inline_first.
do 2 prog_skip.
Qed.
induction ls_b; intuition.
simpl.
prog_simp.
simpl.
eapply comp_spec_ret.
trivial.
simpl.
prog_inline_first.
prog_at prog_inline rightc 1%nat.
simpl.
prog_at prog_ret rightc 2.
simpl.
prog_at prog_swap rightc 1%nat.
Ltac prog_assoc_r :=
eapply comp_spec_eq_trans_r; [ idtac | eapply comp_spec_assoc].
prog_assoc_r.
eapply comp_spec_seq; intuition.
Qed.
(* A bounded repeat based on fold. *)
Definition repeatMax(A : Set)(eqd : EqDec A)(c : Comp A)(P : A -> bool) def (n : nat):=
compFold _ (fun a' i => if (P a') then (ret a') else c) def (allNatsLt n).
induction ls; intuition; simpl.
eapply comp_spec_eq_refl.
rewrite H.
prog_simp.
intuition.
Qed.
induction ls; intuition.
simpl.
intuition.
unfold compFold.
fold compFold.
eapply getSupport_In_Seq.
rewrite H0.
eapply H.
simpl.
intuition.
eapply IHls; intuition.
Qed.
induction 1; intuition.
simpl.
eapply comp_spec_ret.
trivial.
simpl.
case_eq (P1 a); intuition.
assert (P2 b = true).
eapply H3; eauto.
rewrite H5.
prog_simp.
eauto.
case_eq (P2 b); intuition.
assert (P1 a = true).
eapply H3; intuition; eauto.
congruence.
eapply comp_spec_seq; intuition.
Qed.
induction 1; intuition; simpl.
eapply comp_spec_eq_refl.
eapply comp_spec_seq; intuition.
subst.
intuition.
Qed.
induction 1; intuition.
inversion H.
subst.
econstructor.
inversion H1; clear H1; subst.
inversion H2; clear H2; subst.
simpl.
econstructor; intuition.
Qed.
induction lsa; intuition; simpl in *; econstructor; intuition.
Qed.
induction lsa; destruct lsb; intuition; simpl in *; try omega.
econstructor.
econstructor;
intuition.
Qed.
induction 1; intuition; simpl in *.
f_equal.
intuition.
Qed.
intuition.
eapply list_pred_impl.
eapply list_pred_zip_l.
eauto.
eapply list_pred_eq.
eapply list_pred_I.
symmetry.
eapply list_pred_length_eq; eauto.
intuition.
Qed.
induction ls; intuition; simpl in *.
econstructor.
econstructor; intuition.
Qed.
induction 1; intuition; simpl in *.
econstructor.
econstructor; intuition.
econstructor; intuition.
trivial.
Qed.
induction 1; intuition.
inversion H.
subst.
econstructor.
inversion H1; clear H1; subst.
inversion H2; clear H2; subst.
simpl.
econstructor; intuition.
Qed.
induction ls; intuition; simpl in *.
econstructor.
econstructor; intuition.
Qed.
(* map constructed out of fold -- more complicated but allows us to apply any fold theory to map operations.*)
Definition compMap_fold (A B : Set)(eqd : EqDec B)(c : A -> Comp B)(ls : list A) :=
compFold _ (fun (acc : list B)(a : A) => b <-$ c a; ret (acc ++ (b :: nil))) nil ls.
induction lsa; intuition; simpl.
eapply comp_spec_ret; intuition.
eapply comp_spec_seq; intuition.
eapply H0; eauto.
intuition.
Qed.
induction ls; intuition; simpl.
unfold compMap_fold.
simpl.
eapply comp_spec_eq_refl.
unfold compMap_fold.
simpl.
prog_inline_first.
prog_skip.
prog_simp.
eapply comp_spec_eq_trans.
eapply comp_spec_seq_eq; eauto with inhabited.
intuition.
eapply comp_spec_eq_refl.
unfold compMap_fold.
eapply comp_spec_eq_trans_r.
2:{
eapply comp_spec_right_ident.
}
eapply comp_spec_seq; eauto with inhabited.
eapply (@compFold_spec _ _ _ (fun ls p1 p2 => b :: p1 = p2)); intuition.
subst.
simpl.
comp_skip.
eapply comp_spec_ret.
trivial.
intuition.
subst.
eapply comp_spec_eq_refl.
Qed.
induction ls; intuition.
unfold compFold.
wftac.
unfold compFold.
fold compFold.
wftac.
Qed.
induction ls; intuition.
simpl.
eapply comp_spec_eq_refl.
simpl.
prog_simp.
eapply comp_spec_eq_trans.
eapply comp_spec_seq_eq; intuition.
eapply IHls.
eapply comp_spec_eq_refl.
prog_simp.
eapply comp_spec_eq_refl.
Qed.
induction ls; intuition.
simpl in *.
intuition.
unfold compFold in H.
fold compFold in H.
simp_in_support.
assert (x0 = init).
eapply H0; eauto.
simpl.
intuition.
subst.
eapply IHls.
trivial.
intuition.
eapply H0; eauto.
simpl.
intuition.
Qed.
induction ls1; intuition.
rewrite app_nil_l.
unfold compFold.
fold compFold.
comp_simp.
intuition.
rewrite <- app_comm_cons.
unfold compFold.
fold compFold.
inline_first.
comp_skip.
Qed.
intuition.
eapply compFold_eq.
eapply list_pred_eq.
intuition.
subst.
eauto.
Qed.
induction lsa1; intuition; simpl in *.
econstructor.
inversion H; clear H; subst.
econstructor; intuition.
eapply IHlsa1; eauto.
inversion H; clear H; subst.
eapply IHlsa1; eauto.
Qed.
induction lsa; intuition; simpl in *.
inversion H; clear H; subst.
econstructor.
inversion H; clear H; subst.
econstructor.
trivial.
eauto.
Qed.
Definition flatten_prep
(A B : Set)(ls : list (A * (list B))) : list (list (nat * nat * A * B)) :=
map (fun p => [a, lsb] <-2 p; map (fun p' => [i, b] <-2 p'; (i, length lsb, a, b)) (zip (allNatsLt (length lsb)) lsb)) ls.
induction ls1; intuition.
simpl in H.
inversion H; clear H; subst.
unfold compFold.
eapply evalDist_ret_eq.
trivial.
simpl in H.
unfold compFold.
fold compFold.
eapply list_pred_app in H.
intuition.
erewrite <- (@firstn_skipn _ _ ls2).
rewrite compFold_app.
comp_skip.
Qed.
induction 1; intuition.
unfold compMap.
intuition.
unfold compMap. fold compMap.
comp_skip.
comp_skip.
Qed.

Theorem compFold_perm :
forall (A B : Set)(inv : B -> Prop)(ls1 ls2 : list A),
Permutation ls1 ls2 ->
forall (eqd : EqDec B) (c : B -> A -> Comp B) init x,
(forall b a1 a2 x,
In a1 ls1 ->
In a2 ls2 ->
inv b ->
evalDist (b' <-$ c b a1; c b' a2) x ==
evalDist (b' <-$ c b a2; c b' a1) x) ->
(forall a b b',
In a ls1 ->
inv b ->
In b' (getSupport (c b a)) ->
inv b') ->
inv init ->
evalDist (compFold _ c init ls1) x ==
evalDist (compFold _ c init ls2) x.