(* list_pred lifts a predicate on A and B to a predicate on list A and list B.  It is useful for reasoning about fold, map, etc. *)
Inductive list_pred(A B : Set)(pred : A -> B -> Prop) : list A -> list B -> Prop :=
| list_pred_nil : 
    list_pred pred nil nil
| list_pred_cons : 
    forall a1 a2 ls1 ls2,
      pred a1 a2 ->
      list_pred pred ls1 ls2 ->
      list_pred pred (a1 :: ls1) (a2 :: ls2).(* list_pred lifts a predicate on A and B to a predicate on list A and list B.  It is useful for reasoning about fold, map, etc. *)
Inductive list_pred(A B : Set)(pred : A -> B -> Prop) : list A -> list B -> Prop :=
| list_pred_nil : 
    list_pred pred nil nil
| list_pred_cons : 
    forall a1 a2 ls1 ls2,
      pred a1 a2 ->
      list_pred pred ls1 ls2 ->
      list_pred pred (a1 :: ls1) (a2 :: ls2).(* list_pred lifts a predicate on A and B to a predicate on list A and list B.  It is useful for reasoning about fold, map, etc. *)
Inductive list_pred(A B : Set)(pred : A -> B -> Prop) : list A -> list B -> Prop :=
| list_pred_nil : 
    list_pred pred nil nil
| list_pred_cons : 
    forall a1 a2 ls1 ls2,
      pred a1 a2 ->
      list_pred pred ls1 ls2 ->
      list_pred pred (a1 :: ls1) (a2 :: ls2).

(* Specifications: *)
Lemma list_pred_eq_impl_eq : forall (A : Set)(ls1 ls2 : list A), list_pred eq ls1 ls2 -> ls1 = ls2.
Lemma flatten_eq : forall (A : Set)(ls1 ls2 : list (list A)), list_pred eq ls1 ls2 -> flatten ls1 = flatten ls2.
Theorem app_cons_eq : forall (A : Type) ls2 ls1 (a : A), ls2 ++ (a :: ls1) = (ls2 ++ (a :: nil)) ++ ls1.
Theorem skipn_nil : forall (A : Type) n, skipn n (@nil A) = nil.
Theorem nth_In_exists : forall (A : Type)(ls : list A) a def, In a ls -> exists n, nth n ls def = a.
Theorem nth_skipn_eq : forall (A : Set)(y x: nat)(ls : list A)(def : A), nth x (skipn y ls) def = nth (x + y) ls def.
Theorem perm_flatten_listReplace_nil : forall b (A : Set)(a : A), Permutation (flatten (listReplace nil b (a :: nil) nil)) (a :: nil).
Theorem perm_flatten_listReplace : forall b (A : Set)(ls1 : list (list A))(ls2 : list A) (a : A), Permutation (flatten ls1) ls2 -> Permutation (flatten (listReplace ls1 b (nth b ls1 nil ++ (a :: nil)) nil)) (a :: ls2).
Theorem map_cons : forall (A B : Type)(f : A -> B)(ls : list A)(a : A), map f (a :: ls) = (f a) :: map f ls.
Theorem app_eq_inv : forall (A : Type)(ls1 ls2 ls3 ls4 : list A), length ls1 = length ls3 -> (ls1 ++ ls2) = (ls3 ++ ls4) -> ls1 = ls3 /\ ls2 = ls4.
Theorem NoDup_app : forall (A : Type)(ls1 ls2 : list A), NoDup (ls1 ++ ls2) -> NoDup ls1 /\ NoDup ls2 /\ (forall a1 a2, In a1 ls1 -> In a2 ls2 -> a1 <> a2).
Theorem firstn_In : forall (A : Type) n (ls : list A)(a : A), In a (firstn n ls) -> In a ls.
Theorem pred_firstn_In : forall (A : Set) ls1 ls2, list_pred (fun x0 y : list A => exists n : nat, y = firstn n x0) ls1 ls2 -> forall a, In a (flatten ls2) -> In a (flatten ls1).
Theorem firstn_NoDup : forall (A : Type) n (ls : list A), NoDup ls -> NoDup (firstn n ls).
Theorem NoDup_flatten_subset : forall (A : Set)(ls1 ls2 : list (list A)), list_pred (fun x y => exists n, y = firstn n x) ls1 ls2 -> NoDup (flatten ls1) -> NoDup (flatten ls2).
Theorem allNatsLt_nil_inv : forall n, allNatsLt n = nil -> n = O.
Theorem firstn_allNatsLt_h : forall ls n1 n2, n2 >= n1 -> ls = (allNatsLt n2) -> firstn n1 ls = allNatsLt n1.
Theorem firstn_allNatsLt : forall n1 n2, n2 >= n1 -> firstn n1 (allNatsLt n2) = allNatsLt n1.
Theorem NoDup_app_l : forall (A : Type)(ls1 ls2 : list A), NoDup (ls1 ++ ls2) -> NoDup ls1.
Theorem NoDup_map : forall (A B : Type)(f : A -> B)(ls : list A), NoDup (map f ls) -> (NoDup ls /\ (forall b1 b2, In b1 ls -> In b2 ls -> f b1 = f b2 -> b1 = b2)).
Theorem map_fst_eq : forall (C : Set)(lsc : list C)(A B : Set)(ls : list A)(f : A -> B), (length ls = length lsc) -> map f ls = map (fun x => f (fst x)) (combine ls lsc).
Theorem map_snd_eq : forall (C : Set)(lsc : list C)(A B : Set)(ls : list A)(f : A -> B), (length ls = length lsc) -> map f ls = map (fun x => f (snd x)) (combine lsc ls).
Theorem In_combine_NoDup_eq_l : forall (A B : Set)(lsa : list A)(lsb : list B) a1 a2 b, NoDup lsb -> In (a1, b) (combine lsa lsb) -> In (a2, b) (combine lsa lsb) -> a1 = a2.
Theorem In_combine_NoDup_eq_r : forall (A B : Set)(lsa : list A)(lsb : list B) a b1 b2, NoDup lsa -> In (a, b1) (combine lsa lsb) -> In (a, b2) (combine lsa lsb) -> b1 = b2.
Theorem zip_eq_nil_l : forall (A B : Set)(lsa : list A)(lsb : list B), zip lsa lsb = nil -> length lsa = length lsb -> lsa = nil.
Theorem fst_split_app_eq : forall (A B : Type)(ls1 ls2 : list (A * B)), fst (split (ls1 ++ ls2)) = fst (split ls1) ++ fst (split ls2).
Theorem fst_split_flatten_eq : forall (A B : Type)(ls : list (list (A * B))), fst (split (flatten ls)) = flatten (map (fun x => fst (split x)) ls).
Theorem fst_split_map_eq : forall (A B C : Type)(ls : list A)(f : A -> B * C), fst (split (map f ls)) = map (fun a => fst (f a)) ls.
Theorem in_split_l_if : forall (A B : Type)(ls : list (A * B)) a, In a (fst (split ls)) -> exists b, In (a, b) ls.
Theorem in_fst_split_if : forall (A B : Type)(ls : list (A * B)) a b, In (a, b) ls -> In a (fst (split ls)).
Theorem map_pair_fst_eq : forall (A B C D: Type)(f1 : B -> D)(f2 : C -> D)(ls1 : list B)(ls2 : list C)(a1 a2 : A), map (fun x => (a1, f1 x)) ls1 = map (fun x => (a2, f2 x)) ls2 -> ls1 <> nil -> a1 = a2.
Theorem In_zip_strong : forall (A B : Set)(ls : list A) f a (b : B), In (a, b) (zip ls (map f ls)) -> (In a ls /\ b = f a).
Lemma 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 b, P1 a b -> P2 a b) -> list_pred P2 lsa lsb.
Theorem list_pred_eq_in : forall (A : Set)(ls : list A), list_pred (fun a b => a = b /\ In a ls /\ In b ls) ls ls.
Theorem zip_combine_eq : forall (A B : Set)(lsa : list A)(lsb : list B), zip lsa lsb = combine lsa lsb.
Theorem list_pred_fst_split_eq : forall (A B C : Set)(ls1 : list (A * B))(ls2 : list (A * C)), list_pred (fun a b => fst a = fst b) ls1 ls2 -> fst (split ls1) = fst (split ls2).
Theorem unzip_eq_split : forall (A B : Set)(ls : list (A * B)), unzip ls = split ls.
Theorem in_split_r_if: forall (A B : Type) (ls : list (A * B)) (b : B), In b (snd (split ls)) -> exists a : A, In (a, b) ls.