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 *.

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'.