Section Elt3.
(** * [map2] *)
Variable elt elt' elt'' : Type.
Variable f : option elt -> option elt' -> option elt''.
Definition option_cons (A:Type)(k:key)(o:option A)(l:list (key*A)) :=
match o with
| Some e => (k,e)::l
| None => l
end.
Fixpoint map2_l (m : t elt) : t elt'' :=
match m with
| nil => nil
| (k,e)::l => option_cons k (f (Some e) None) (map2_l l)
end.
Fixpoint map2_r (m' : t elt') : t elt'' :=
match m' with
| nil => nil
| (k,e')::l' => option_cons k (f None (Some e')) (map2_r l')
end.
Fixpoint map2 (m : t elt) : t elt' -> t elt'' :=
match m with
| nil => map2_r
| (k,e) :: l =>
fix map2_aux (m' : t elt') : t elt'' :=
match m' with
| nil => map2_l m
| (k',e') :: l' =>
match X.compare k k' with
| LT _ => option_cons k (f (Some e) None) (map2 l m')
| EQ _ => option_cons k (f (Some e) (Some e')) (map2 l l')
| GT _ => option_cons k' (f None (Some e')) (map2_aux l')
end
end
end.
Notation oee' := (option elt * option elt')%type.
Fixpoint combine (m : t elt) : t elt' -> t oee' :=
match m with
| nil => map (fun e' => (None,Some e'))
| (k,e) :: l =>
fix combine_aux (m':t elt') : list (key * oee') :=
match m' with
| nil => map (fun e => (Some e,None)) m
| (k',e') :: l' =>
match X.compare k k' with
| LT _ => (k,(Some e, None))::combine l m'
| EQ _ => (k,(Some e, Some e'))::combine l l'
| GT _ => (k',(None,Some e'))::combine_aux l'
end
end
end.
Definition fold_right_pair (A B C:Type)(f: A->B->C->C)(l:list (A*B))(i:C) :=
List.fold_right (fun p => f (fst p) (snd p)) i l.
Definition map2_alt m m' :=
let m0 : t oee' := combine m m' in
let m1 : t (option elt'') := map (fun p => f (fst p) (snd p)) m0 in
fold_right_pair (option_cons (A:=elt'')) m1 nil.
Hint Resolve combine_lelistA : core.
Definition at_least_one (o:option elt)(o':option elt') :=
match o, o' with
| None, None => None
| _, _  => Some (o,o')
end.
Definition at_least_one_then_f (o:option elt)(o':option elt') :=
match o, o' with
| None, None => None
| _, _  => f o o'
end.

Lemma map2_0 :
forall m (Hm : sort (@ltk elt) m) m' (Hm' : sort (@ltk elt') m') (x:key),
find x (map2 m m') = at_least_one_then_f (find x m) (find x m').