Ltac prog_assoc_r :=
    eapply comp_spec_eq_trans_r; [ idtac | eapply comp_spec_assoc].(* 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).

(* Specifications: *)
Lemma repeatMax_fold_true : forall (A B: Set)(ls : list B)(eqd : EqDec A)(c : B -> Comp A)(P : A -> bool)(a : A), P a = true -> comp_spec eq (compFold _ (fun acc b => if (P acc) then (ret acc) else (c b)) a ls) (ret a).
Lemma repeatMax_fold_in_support_false : forall (A B : Set)(ls : list B)(eqd : EqDec A)(c : B -> Comp A)(P : A -> bool)(a : A), (forall b, In b ls -> In a (getSupport (c b))) -> P a = false -> In a (getSupport (compFold eqd (fun (a' : A) b => if P a' then ret a' else (c b)) a ls)).
Theorem compFold_repeat_spec : forall (C D: Set)(ls1 : list C)(ls2 : list D)(pre : C -> D -> Prop), list_pred pre ls1 ls2 -> forall (A B : Set)(eqda : EqDec A)(eqdb : EqDec B)(post : A -> B -> Prop)(c1 : C -> Comp A)(P1 : A -> bool)(c2 : D -> Comp B)(P2 : B -> bool) a b, post a b -> (forall a b, pre a b -> comp_spec post (c1 a) (c2 b)) -> (forall a b, post a b -> (P1 a = true <-> P2 b = true)) -> comp_spec post (compFold _ (fun acc v => if (P1 acc) then (ret acc) else (c1 v)) a ls1) (compFold _ (fun acc v => if (P2 acc) then (ret acc) else (c2 v)) b ls2).
Theorem compFold_eq : forall (A1 A2 : Set) P (ls1 : list A1) (ls2 : list A2), list_pred P ls1 ls2 -> forall (B : Set)(eqd : EqDec B) (c1 : B -> A1 -> Comp B) (c2: B -> A2 -> Comp B), (forall acc a1 a2, P a1 a2 -> In a1 ls1 -> In a2 ls2 -> comp_spec eq (c1 acc a1) (c2 acc a2) ) -> forall init, comp_spec eq (compFold _ c1 init ls1) (compFold _ c2 init ls2).
Lemma list_pred_zip_l : forall (A B: Set)(lsa : list A)(lsb : list B)(P : A -> B -> Prop), list_pred P lsa lsb -> forall (C : Set)(lsc : list C)(P1 : A -> C -> Prop)(P2 : B -> C -> Prop), list_pred P1 lsa lsc -> list_pred P2 lsb lsc -> list_pred (fun (p : A * B) c => P (fst p) (snd p) /\ P1 (fst p) c /\ P2 (snd p) c) (zip lsa lsb) lsc.
Lemma list_pred_eq : forall (A : Set)(lsa : list A), list_pred eq lsa lsa.
Lemma list_pred_I : forall (A B : Set)(lsa : list A)(lsb : list B), (length lsa = length lsb) -> list_pred (fun a b => True) lsa lsb.
Lemma list_pred_length_eq : forall (A B : Set)(lsa : list A)(lsb : list B)(P : A -> B -> Prop), list_pred P lsa lsb -> length lsa = length lsb.
Lemma list_pred_zip_l_eq : forall (A B: Set)(lsa : list A)(lsb : list B)(P : A -> B -> Prop), list_pred P lsa lsb -> list_pred (fun p a => (fst p) = a /\ P (fst p) (snd p)) (zip lsa lsb) lsa.
Lemma list_pred_map_r_eq : forall (A B : Set)(ls : list A)(f : A -> B), list_pred (fun a b => f a = b) ls (map f ls).
Lemma list_pred_map_r : forall (A B : Set)(lsa : list A)(lsb : list B)(P : A -> B -> Prop), list_pred P lsa lsb -> forall (C : Set)(f : B -> C), list_pred (fun a c => exists b, P a b /\ c = f b) lsa (map f lsb).
Lemma list_pred_zip_r : forall (A B: Set)(lsa : list A)(lsb : list B)(P : A -> B -> Prop), list_pred P lsa lsb -> forall (C : Set)(lsc : list C)(P1 : A -> C -> Prop)(P2 : B -> C -> Prop), list_pred P1 lsa lsc -> list_pred P2 lsb lsc -> list_pred (fun c (p : A * B) => P (fst p) (snd p) /\ P1 (fst p) c /\ P2 (snd p) c) lsc (zip lsa lsb).
Lemma list_pred_map_l_eq : forall (A B : Set)(ls : list A)(f : A -> B), list_pred (fun b a => f a = b) (map f ls) ls.
Lemma compFold_spec : forall (A C D : Set)(P2 : list A -> C -> D -> Prop)(eqdc : EqDec C)(eqdd : EqDec D)(lsa : list A)(c1 : C -> A -> Comp C)(c2 : D -> A -> Comp D) init1 init2, P2 lsa init1 init2 -> (forall a lsa c d, P2 (a :: lsa) c d -> comp_spec (P2 lsa) (c1 c a) (c2 d a)) -> comp_spec (P2 nil) (compFold _ c1 init1 lsa) (compFold _ c2 init2 lsa).
Theorem compMap_fold_equiv : forall (A B : Set)(eqd : EqDec B)(c : A -> Comp B)(ls : list A), comp_spec eq (compMap eqd c ls) (compMap_fold eqd c ls).
Lemma compFold_wf : forall (A B : Set)(eqd : EqDec A)(c : A -> B -> Comp A)(ls : list B) init, (forall a b, well_formed_comp (c a b)) -> well_formed_comp (compFold _ c init ls).
Lemma compMap_map : forall (A B : Set)(eqd : EqDec B)(f : A -> B) ls, comp_spec eq (compMap _ (fun a => ret (f a)) ls) (ret (map f ls)).
Lemma compFold_nop : forall (A B : Set)(eqd : EqDec A)(c : A -> B -> Comp A)(ls : list B) init x, In x (getSupport (compFold _ c init ls)) -> (forall b a, In b ls -> In a (getSupport (c init b)) -> a = init) -> x = init.
Theorem comp_fold_ext : forall (A B : Set)(eqd : EqDec A)(c1 c2 : A -> B -> Comp A)(ls : list B)(init : A), (forall a b, comp_spec eq (c1 a b) (c2 a b)) -> comp_spec eq (compFold _ c1 init ls) (compFold _ c2 init ls).
Lemma list_pred_app : forall (A B : Set)(P : A -> B -> Prop)(lsa1 lsa2 : list A)(lsb : list B), list_pred P (lsa1 ++ lsa2) lsb -> list_pred P lsa1 (firstn (length lsa1) lsb) * list_pred P lsa2 (skipn (length lsa1) lsb).
Theorem list_pred_map_l_inv : forall (A B C : Set) (lsa : list A) (lsb : list B) (f : A -> C)(P : C -> B -> Prop), list_pred P (map f lsa) lsb -> list_pred (fun a b => P (f a) b) lsa lsb.
Lemma list_pred_app_both : forall (A B : Set)(P : A -> B -> Prop)(lsa1 lsa2 : list A)(lsb1 lsb2 : list B), list_pred P lsa1 lsb1 -> list_pred P lsa2 lsb2 -> list_pred P (lsa1 ++ lsa2) (lsb1 ++ lsb2).
Lemma list_pred_map_both : forall (A B C D : Set)(P : A -> B -> Prop)(lsa : list A)(lsb : list B)(f1 : A -> C)(f2 : B -> D), list_pred P lsa lsb -> list_pred (fun c d => exists a b, P a b /\ c = (f1 a) /\ d = (f2 b)) (map f1 lsa) (map f2 lsb).
Lemma compMap_spec : forall (A B C D : Set)(eqdc : EqDec C)(eqdd : EqDec D)(P1 : A -> B -> Prop)(P2 : C -> D -> Prop)(lsa : list A)(lsb : list B)(c1 : A -> Comp C)(c2 : B -> Comp D), list_pred P1 lsa lsb -> (forall a b, In a lsa -> In b lsb -> P1 a b -> comp_spec P2 (c1 a) (c2 b)) -> comp_spec (list_pred P2) (compMap _ c1 lsa) (compMap _ c2 lsb).
Lemma list_pred_eq_gen : forall (A : Set)(ls1 ls2 : list A), ls1 = ls2 -> list_pred eq ls1 ls2.
Lemma compMap_support : forall (A B : Set)(P : A -> B -> Prop)(eqd : EqDec B)(c : A -> Comp B)(lsa : list A)(lsb : list B), In lsb (getSupport (compMap _ c lsa)) -> (forall a b, In a lsa -> In b (getSupport (c a)) -> P a b) -> list_pred P lsa lsb.
Lemma In_zip : forall (A B : Set)(a : A)( b : B) lsa lsb, In (a, b) (zip lsa lsb) -> In a lsa /\ In b lsb.
Lemma list_pred_map_l : forall (A B : Set) (lsa : list A) (lsb : list B) (P : A -> B -> Prop), list_pred P lsa lsb -> forall (C : Set) (f : A -> C), list_pred (fun (c : C) (b : B) => exists a : A, P a b /\ c = f a) (map f lsa) lsb.
Lemma list_pred_map_l_if : forall (A B C : Set) (P : C -> B -> Prop)(lsa : list A)(lsb : list B)(f : A -> C), list_pred P (map f lsa) lsb -> list_pred (fun a b => P (f a) b) lsa lsb.
Lemma list_pred_map_r_if : forall (A B C : Set) (P : A -> C -> Prop)(lsb : list B)(lsa : list A)(f : B -> C), list_pred P lsa (map f lsb) -> list_pred (fun a b => P a (f b)) lsa lsb.
Lemma list_pred_map_r' : forall (A B C : Set) (lsa : list A) (lsb : list B) (P : A -> C -> Prop) (f : B -> C), list_pred (fun a b => P a (f b)) lsa lsb -> list_pred P lsa (map f lsb).
Lemma list_pred_map_l' : forall (A B C : Set) (lsa : list A) (lsb : list B) (P : C -> B -> Prop) (f : A -> C), list_pred (fun a b => P (f a) b) lsa lsb -> list_pred P (map f lsa) lsb.
Theorem list_pred_rev : forall (A B : Set)(lsa : list A)(lsb : list B) P, list_pred P lsa lsb -> forall a b, P a b -> list_pred P (lsa ++ (a :: nil)) (lsb ++ (b :: nil)).
Theorem list_pred_impl' : forall (A B : Set) (lsa : list A) (lsb : list B) (P1 : A -> B -> Prop), list_pred P1 lsa lsb -> forall P2 : A -> B -> Prop, (forall (a : A) (b : B), In a lsa -> In b lsb -> P1 a b -> P2 a b) -> list_pred P2 lsa lsb.
Lemma list_pred_allNatsLt : forall (A : Set)(ls : list A), list_pred (fun i a => forall a', nth i ls a' = a) (allNatsLt (length ls)) ls.
Lemma compMap_length : forall (A B : Set)(eqd : EqDec B)(ls : list A) x (c : A -> Comp B) , In x (getSupport (compMap _ c ls)) -> length x = length ls.
Lemma map_eq : forall (A B C : Set)(lsa : list A)(lsb : list B)(f1 : A -> C)(f2 : B -> C), list_pred (fun a b => f1 a = f2 b) lsa lsb -> map f1 lsa = map f2 lsb.
Lemma zip_length : forall (A B : Set)(lsa : list A)(lsb : list B), length lsa = length lsb -> length (zip lsa lsb) = length lsa.
Lemma numberedMap_length : forall (A B : Set)(ls : list A)(f : nat -> nat -> A -> B), length (numberedMap f ls) = length ls.
Lemma nth_zip : forall (A B : Set)(lsa : list A)(lsb : list B) n a b defa defb, length lsa = length lsb -> nth n (zip lsa lsb) (defa, defb) = (a, b) -> nth n lsa defa = a /\ nth n lsb defb = b.
Lemma compFold_spec' : forall (A B C D : Set)(P2 : list A -> list B -> C -> D -> Prop)(eqdc1 eqdc2 : EqDec C)(eqdd1 eqdd2 : EqDec D)(lsa : list A)(lsb : list B)(c1 : C -> A -> Comp C)(c2 : D -> B -> Comp D) init1 init2, length lsa = length lsb -> P2 lsa lsb init1 init2 -> (forall a lsa b lsb c d, P2 (a :: lsa) (b :: lsb) c d -> comp_spec (P2 lsa lsb) (c1 c a) (c2 d b)) -> @comp_spec _ _ eqdc1 eqdd1 (P2 nil nil) (compFold eqdc2 c1 init1 lsa) (compFold eqdd2 c2 init2 lsb).
Lemma compMap_support_app : forall (A B : Set)(eqd : EqDec B)(c : A -> Comp B)(lsa1 lsa2 : list A)(lsb1 lsb2 : list B), In lsb1 (getSupport (compMap _ c lsa1)) -> In lsb2 (getSupport (compMap _ c lsa2)) -> In (lsb1 ++ lsb2) (getSupport (compMap _ c (lsa1 ++ lsa2))).
Lemma list_pred_single : forall (A : Set) (P : A -> A -> Prop) (lsa : list A), (forall a, In a lsa -> P a a) -> list_pred P lsa lsa.
Lemma flatten_map_eq : forall (A B : Set)(ls : list (list A))(f : A -> B), map f (flatten ls) = flatten (map (fun ls' => map f ls') ls).
Lemma zip_app : forall (A B : Set)(lsa1 lsa2 : list A)(lsb1 lsb2 : list B), length lsa1 = length lsb1 -> length lsa2 = length lsb2 -> (zip lsa1 lsb1) ++ (zip lsa2 lsb2) = (zip (lsa1 ++ lsa2) (lsb1 ++ lsb2)).
Lemma length_flatten_eq : forall (A B : Set)(lsa : list (list A))(lsb : list (list B)), list_pred (fun a1 a2 => length a1 = length a2) lsa lsb -> length (flatten lsa) = length (flatten lsb).
Lemma compMap_flatten : forall (A B : Set)(eqd : EqDec B)(c : A -> Comp B)(ls : list (list A)), comp_spec (fun ls1 ls2 => ls2 = flatten ls1) (compMap _ (fun ls' => compMap _ c ls') ls) (compMap _ c (flatten ls)).
Lemma map_f_equal : forall (A B : Set)(f1 f2 : A -> B)(ls1 ls2 : list A), (forall a, f1 a = f2 a) -> ls1 = ls2 -> map f1 ls1 = map f2 ls2.
Lemma list_pred_zip_in : forall (A B : Set)(P : A -> B -> Prop)(lsa : list A)(lsb : list B), list_pred P lsa lsb -> forall a b, In (a, b) (zip lsa lsb) -> P a b.
Lemma in_zip_swap : forall (A B : Set)(lsa : list A)(lsb : list B) a b, length lsa = length lsb -> In (a, b) (zip lsa lsb) -> In (b, a) (zip lsb lsa).
Lemma unzip_zip_inv : forall (A B : Set)(lsa : list A)(lsb : list B), length lsa = length lsb -> unzip (zip lsa lsb) = (lsa, lsb).