Ltac update2_destruct_goal :=
  match goal with
  | [ |- context [ update2 ?eq_dec _ ?x ?y _ ?x' ?y' ] ] =>
    destruct (prod_eq_dec eq_dec eq_dec (x, y) (x', y'))
  end.Ltac update2_destruct_hyp :=
  match goal with
  | [ _ : context [ update2 ?eq_dec _ ?x ?y _ ?x' ?y' ] |- _ ] =>
    destruct (prod_eq_dec eq_dec eq_dec (x, y) (x', y'))
  end.Ltac update2_destruct := update2_destruct_goal || update2_destruct_hyp.Ltac rewrite_update2' H :=
  first [rewrite update2_diff_prod in H by congruence |
         rewrite update2_eq_prod in H by auto ].Ltac rewrite_update2 :=
  repeat match goal with
           | [ H : context [ update2 _ _ _ _ _ _ _ ] |- _ ] =>
             rewrite_update2' H; repeat rewrite_update2' H
           | [ |- _ ] => repeat (try rewrite update2_diff_prod by congruence;
                                 try rewrite update2_eq_prod by auto)
         end.Ltac destruct_update2 :=
  repeat (update2_destruct; subst; rewrite_update2).Ltac destruct_update2_hyp :=
  repeat ((update2_destruct_hyp || update2_destruct_goal); subst; rewrite_update2).Ltac update2_destruct_simplify :=
  update2_destruct; subst; rewrite_update2; simpl in *.Ltac update2_destruct_simplify_hyp :=
  update2_destruct_hyp || update2_destruct_goal; subst; rewrite_update2; simpl in *.Ltac update2_destruct_max_simplify :=
  update2_destruct; subst_max; rewrite_update2; simpl in *.

(* Specifications: *)
Lemma filter_rel_related : forall n n' ns, In n' (filter_rel R_dec n ns) -> In n' ns /\ R n n'.
Lemma related_filter_rel : forall n n' ns, In n' ns -> R n n' -> In n' (filter_rel R_dec n ns).
Lemma not_in_not_in_filter_rel : forall ns n h, ~ In n ns -> ~ In n (filter_rel R_dec h ns).
Lemma NoDup_filter_rel: forall h ns, NoDup ns -> NoDup (filter_rel R_dec h ns).
Lemma filter_rel_self_eq {irreflexive_R : Irreflexive R} : forall ns0 ns1 h, filter_rel R_dec h (ns0 ++ h :: ns1) = filter_rel R_dec h (ns0 ++ ns1).
Lemma collate_f_eq : forall (f : A -> A -> list B) g h n n' l, f n n' = g n n' -> collate A_eq_dec h f l n n' = collate A_eq_dec h g l n n'.
Lemma collate_in_in : forall l h n n' (f : A -> A -> list B) a, In a (f n' n) -> In a ((collate A_eq_dec h f l) n' n).
Lemma collate_head_head : forall l h n n' (f : A -> A -> list B) a, head (f n' n) = Some a -> head ((collate A_eq_dec h f l) n' n) = Some a.
Lemma collate_neq : forall h n n' ns (f : A -> A -> list B), h <> n -> collate A_eq_dec h f ns n n' = f n n'.
Lemma collate_not_in_eq : forall h' h (f : A -> A -> list B) l, ~ In h (map fst l) -> collate A_eq_dec h' f l h' h = f h' h.
Lemma collate_app : forall h' l1 l2 (f : A -> A -> list B), collate A_eq_dec h' f (l1 ++ l2) = collate A_eq_dec h' (collate A_eq_dec h' f l1) l2.
Lemma collate_neq_update2 : forall h h' n (f : A -> A -> list B) l ms, n <> h' -> collate A_eq_dec h (update2 A_eq_dec f h n ms) l h h' = collate A_eq_dec h f l h h'.
Lemma collate_not_in : forall h h' l1 l2 (f : A -> A -> list B), ~ In h' (map fst l1) -> collate A_eq_dec h f (l1 ++ l2) h h' = collate A_eq_dec h f l2 h h'.
Lemma collate_not_in_rest : forall h h' l1 l2 (f : A -> A -> list B), ~ In h' (map fst l2) -> collate A_eq_dec h f (l1 ++ l2) h h' = collate A_eq_dec h f l1 h h'.
Lemma collate_not_in_mid : forall h h' l1 l2 (f : A -> A -> list B) m, ~ In h (map fst (l1 ++ l2)) -> collate A_eq_dec h' (update2 A_eq_dec f h' h (f h' h ++ [m])) (l1 ++ l2) = collate A_eq_dec h' f (l1 ++ (h, m) :: l2).
Lemma NoDup_Permutation_collate_eq : forall h (f : A -> A -> list B) l l', NoDup (map fst l) -> Permutation l l' -> collate A_eq_dec h f l = collate A_eq_dec h f l'.
Lemma collate_map2snd_not_related : forall m n h ns (f : A -> A -> list B), ~ R h n -> collate A_eq_dec h f (map2snd m (filter_rel R_dec h ns)) h n = f h n.
Lemma collate_map2snd_in_neq_in_before : forall from (f : A -> A -> list B) m dsts a b x, In x (collate A_eq_dec from f (map2snd m dsts) a b) -> x <> m -> In x (f a b).
Lemma collate_map2snd_not_in : forall m n h ns (f : A -> A -> list B), ~ In n ns -> collate A_eq_dec h f (map2snd m (filter_rel R_dec h ns)) h n = f h n.
Lemma collate_map2snd_not_in_remove_all : forall m n h ns (f : A -> A -> list B) ns', ~ In n ns -> collate A_eq_dec h f (map2snd m (filter_rel R_dec h (remove_all A_eq_dec ns' ns))) h n = f h n.
Lemma collate_map2snd_not_in_related : forall m n h ns (f : A -> A -> list B) ns', ~ In n ns' -> R h n -> In n ns -> NoDup ns -> collate A_eq_dec h f (map2snd m (filter_rel R_dec h (remove_all A_eq_dec ns' ns))) h n = f h n ++ [m].
Lemma collate_map2snd_in : forall m n h ns (f : A -> A -> list B) ns', In n ns' -> collate A_eq_dec h f (map2snd m (filter_rel R_dec h (remove_all A_eq_dec ns' ns))) h n = f h n.
Lemma collate_map2snd_related_not_in : forall a n h ns (f : A -> A -> list B), R h n -> ~ In n ns -> collate A_eq_dec h f (map2snd a (filter_rel R_dec h (n :: ns))) h n = f h n ++ [a].
Lemma in_collate_in : forall ns n h (f : A -> A -> list B) a, ~ In n ns -> In a (collate A_eq_dec h f (map2snd a (filter_rel R_dec h ns)) h n) -> In a (f h n).
Lemma not_in_map2snd : forall n h (m : B) ns, ~ In n ns -> ~ In (n, m) (map2snd m (filter_rel R_dec h ns)).
Lemma NoDup_map2snd : forall h (m : B) ns, NoDup ns -> NoDup (map2snd m (filter_rel R_dec h ns)).
Lemma in_map2snd_snd : forall h (m : B) ns nm, In nm (map2snd m (filter_rel R_dec h ns)) -> snd nm = m.
Lemma in_map2snd_related_in : forall (m : B) ns n h, In (n, m) (map2snd m (filter_rel R_dec h ns)) -> R h n /\ In n ns.
Lemma collate_ls_not_in : forall ns (f : A -> A -> list B) h mg from to, ~ In from ns -> collate_ls A_eq_dec ns f h mg from to = f from to.
Lemma collate_ls_in_in : forall ns (f : A -> A -> list B) to m x a b, In x (f a b) -> In x (collate_ls A_eq_dec ns f to m a b).
Lemma collate_ls_neq_to : forall ns (f : A -> A -> list B) h mg from to, h <> to -> collate_ls A_eq_dec ns f h mg from to = f from to.
Lemma collate_ls_NoDup_in : forall ns (f : A -> A -> list B) h mg from, NoDup ns -> In from ns -> collate_ls A_eq_dec ns f h mg from h = f from h ++ [mg].
Lemma collate_ls_live_related : forall ns (f : A -> A -> list B) ns' h mg from, ~ In from ns' -> R h from -> In from ns -> NoDup ns -> collate_ls A_eq_dec (filter_rel R_dec h (remove_all A_eq_dec ns' ns)) f h mg from h = f from h ++ [mg].
Lemma collate_ls_f_eq : forall ns (f : A -> A -> list B) g h mg n n', f n n' = g n n' -> collate_ls A_eq_dec ns f h mg n n' = collate_ls A_eq_dec ns g h mg n n'.
Lemma collate_ls_neq_update2 : forall ns (f : A -> A -> list B) n h h' ms mg, n <> h' -> collate_ls A_eq_dec ns (update2 A_eq_dec f n h ms) h mg h' h = collate_ls A_eq_dec ns f h mg h' h.
Lemma collate_ls_cases : forall s (f : A -> A -> list B) to m a b, collate_ls A_eq_dec s f to m a b = f a b \/ exists l, (forall x, In x l -> x = m) /\ collate_ls A_eq_dec s f to m a b = f a b ++ l.
Lemma collate_ls_in_neq_in_before : forall s (f : A -> A -> list B) to m a b x, In x (collate_ls A_eq_dec s f to m a b) -> x <> m -> In x (f a b).
Lemma collate_ls_not_related : forall ns (f : A -> A -> list B) n h mg, ~ R h n -> collate_ls A_eq_dec (filter_rel R_dec h ns) f h mg n h = f n h.
Lemma collate_ls_not_in_related : forall ns (f : A -> A -> list B) n h mg, ~ In n ns -> collate_ls A_eq_dec (filter_rel R_dec h ns) f h mg n h = f n h.
Lemma collate_ls_not_in_related_remove_all : forall ns (f : A -> A -> list B) n h mg ns', ~ In n ns -> collate_ls A_eq_dec (filter_rel R_dec h (remove_all A_eq_dec ns' ns)) f h mg n h = f n h.
Lemma collate_ls_in_remove_all : forall mg n h ns (f : A -> A -> list B) ns', In n ns' -> collate_ls A_eq_dec (filter_rel R_dec h (remove_all A_eq_dec ns' ns)) f h mg n h = f n h.
Lemma collate_ls_app : forall l1 l2 (f : A -> A -> list B) h m, collate_ls A_eq_dec (l1 ++ l2) f h m = collate_ls A_eq_dec l2 (collate_ls A_eq_dec l1 f h m) h m.
Lemma collate_ls_split_eq : forall l1 l2 (f : A -> A -> list B) h m from to, h <> from -> collate_ls A_eq_dec (l1 ++ h :: l2) f to m from to = collate_ls A_eq_dec (l1 ++ l2) f to m from to.
Lemma collate_ls_not_in_mid : forall h h' l1 l2 (f : A -> A -> list B) m, ~ In h' (l1 ++ l2) -> collate_ls A_eq_dec (l1 ++ l2) (update2 A_eq_dec f h' h (f h' h ++ [m])) h m = collate_ls A_eq_dec (l1 ++ h' :: l2) f h m.
Lemma NoDup_Permutation_collate_ls_eq : forall l (f : A -> A -> list B) h m l', NoDup l -> Permutation l l' -> collate_ls A_eq_dec l f h m = collate_ls A_eq_dec l' f h m.