(* Given two list returns the list of possible product of an element
   of the first list with an element of the second list *)
 
Fixpoint mProd (A B C : Set) (l1 : list A) (l2 : list B) {struct l2} :
 list (A * B) :=
  match l2 with
  | nil => nil
  | b :: l2' => map (fun a : A => (a, b)) l1 ++ mProd A B C l1 l2'
  end.

(* Specifications: *)
Theorem mProd_correct : forall (A B C : Set) (l1 : list A) (l2 : list B) (a : A) (b : B), In a l1 -> In b l2 -> In (a, b) (mProd A B C l1 l2).
Theorem mProd_correct_rev1 : forall (A B C : Set) (l1 : list A) (l2 : list B) (a : A) (b : B), In (a, b) (mProd A B C l1 l2) -> In a l1.
Theorem mProd_correct_rev2 : forall (A B C : Set) (l1 : list A) (l2 : list B) (a : A) (b : B), In (a, b) (mProd A B C l1 l2) -> In b l2.
Theorem in_map_inv : forall (A B : Set) (f : A -> B) (l : list A) (x : A), (forall a b : A, f a = f b -> a = b) -> In (f x) (map f l) -> In x l.