Section splits_and_perms.
Context {T: Type}.
Fixpoint splits (l: list T): list (T * list T) :=
match l with
| nil => nil
| h :: t => (h, t) :: map (fun xy => (fst xy, h :: snd xy)) (splits t)
end.
Proof with auto.
induction l...
simpl.
rewrite map_length.
intuition.
Qed.
Proof with auto.
induction l in p |- *...
simpl.
intuition.
simpl.
intros.
destruct H.
subst...
destruct (fst (conj_prod (in_map_iff _ _ _)) H).
destruct H0.
apply IHl in H1.
subst.
simpl...
apply perm_trans with (a :: fst x :: snd x).
apply perm_swap.
apply perm_skip...
Qed.
Fixpoint insert_everywhere (x: T) (l: list T): list (list T) :=
match l with
| nil => (x :: nil) :: nil
| h :: t => (x :: h :: t) :: map (cons h) (insert_everywhere x t)
end.
Proof with auto.
induction l...
intros.
simpl in H.
intuition.
subst...
intros.
simpl in H.
destruct H.
subst...
apply in_map_iff in H. destruct H as [x0 [A B]].
subst.
apply IHl in B.
eauto.
Qed.
Proof with auto.
induction l...
simpl.
rewrite map_length.
congruence.
Qed.
Definition perms: list T -> list (list T)
:= fold_right (fun h => concat ∘ map (insert_everywhere h)) (nil :: nil).
Proof with auto.
induction l in a |- *.
simpl.
intuition.
subst...
intros.
simpl in H.
unfold Basics.compose in H.
apply In_concat_inv in H. destruct H. destruct H.
apply in_map_iff in H0. destruct H0 as [x0 [A B]].
subst.
eauto using insert_everywhere_are_perms.
Qed.
Proof with auto.
induction l...
simpl.
unfold Basics.compose.
rewrite length_concat.
rewrite map_map.
rewrite (@add_same (S (length l)) (map (fun x => length (insert_everywhere a x)) (perms l))).
rewrite map_length.
rewrite IHl.
rewrite Mult.mult_comm...
intros.
destruct (in_map_iff (fun x => length (insert_everywhere a x)) (perms l) x).
clear H1.
apply H0 in H. clear H0.
destruct H. destruct H.
subst.
rewrite length_insert_everywhere.
f_equal.
apply Permutation_length.
apply perms_are_perms...
Qed.
Definition alt_perms l: list (list T) :=
match l with
| nil => nil :: nil
| _ => concat (map (fun p => (map (cons (fst p)) (perms (snd p)))) (splits l))
end.
Proof with auto.
intro.
set (fun x y: T * list T => fst x = fst y /\ Permutation (snd x) (snd y)).
intros.
induction H.
simpl...
simpl.
apply permuted_skip...
split...
apply Permuted_map.
unfold on.
subst P.
simpl.
apply (Permuted_sub IHPermutation).
intuition.
simpl.
subst P.
apply alt_permuted_swap...
repeat rewrite map_map.
simpl.
apply elemsR_map_map.
simpl...
eauto.
Qed.
Inductive merges_spec: list T -> list T -> list (list T) -> Prop :=
| merges_left_nil x: merges_spec nil x (x :: nil)
| merges_right_nil x: merges_spec x nil (x :: nil)
| merges_cons x y h t r r':
merges_spec y (h :: t) r ->
merges_spec (x :: y) t r' ->
merges_spec (x :: y) (h :: t) (map (cons x) r ++ map (cons h) r').
Hint Constructors merges_spec.
Proof with auto.
intros H.
induction H; intros.
inversion_clear H...
inversion_clear H...
inversion_clear H1.
apply IHmerges_spec1 in H2.
apply IHmerges_spec2 in H3.
congruence.
Qed.
Proof with auto.
intros.
induction H2.
simpl...
simpl...
rewrite app_length.
repeat rewrite map_length.
rewrite IHmerges_spec1. clear IHmerges_spec1.
rewrite IHmerges_spec2. clear IHmerges_spec2.
simpl @length...
Qed.
Definition me (ab: list T * list T): nat := length (fst ab) + length (snd ab).
Program Fixpoint merges_ex (ab: list T * list T) {measure (me ab)}: sig (merges_spec (fst ab) (snd ab)) :=
match ab with
| (nil, x) => x :: nil
| (x, nil) => x :: nil
| (x :: y, h :: t) => map (cons x) (merges_ex (y, h :: t)) ++ map (cons h) (merges_ex (x :: y, t))
end.
Next Obligation. unfold me. simpl. omega. Qed.
Next Obligation. repeat destruct_call merges_ex; auto. Qed.
Definition merges (a b: list T): list (list T) := proj1_sig (merges_ex (a, b)).
Proof with auto.
intros.
apply (@merges_uniq a b).
unfold merges. destruct_call merges_ex...
destruct a... destruct b...
unfold merges. repeat destruct_call merges_ex...
Qed.
Proof with auto.
intros.
rewrite merges_real_eq.
destruct a...
Qed.
Hint Resolve Permutation_concat.
Proof with auto.
intros.
unfold product.
rewrite map_app.
rewrite concat_app...
Qed.

Lemma product_concat: forall T (a: list (list T)) (b: list T), product (concat a) b = concat (map (flip (@product _ _) b) a).