(** Some constants that return their first argument, and hold an equal second argument. *)
Require Import Crypto.Util.Tuple.
Definition id_with_alt {A} (value : A) (value_for_alt : A) : A
:= value.
Definition id_with_alt_proof {A} (value : A) (value_for_alt : A)
           {pf : value = value_for_alt}
  : A
  := id_with_alt value value_for_alt.

Section tuple.
  Context {A : Type}.

  Fixpoint id_tuple'_with_alt {n}
    : forall (value value_for_alt : tuple' A n),
      tuple' A n
    := match n return forall value value_for_alt : tuple' A n, tuple' A n with
       | O => id_with_alt
       | S n' => fun (value value_for_alt : tuple' A n' * A)
                 => (@id_tuple'_with_alt n' (fst value) (fst value_for_alt),
                     @id_with_alt A (snd value) (snd value_for_alt))
       end.

  Fixpoint id_tuple'_with_alt_proof {n}
    : forall (value value_for_alt : tuple' A n) {pf : value = value_for_alt},
      tuple' A n
    := match n return forall value value_for_alt : tuple' A n, _ -> tuple' A n with
       | O => id_with_alt_proof
       | S n' => fun (value value_for_alt : tuple' A n' * A) (pf : value = value_for_alt)
                 => (@id_tuple'_with_alt_proof n' (fst value) (fst value_for_alt)
                                               (f_equal (@fst _ _) pf),
                     @id_with_alt_proof A (snd value) (snd value_for_alt)
                                        (f_equal (@snd _ _) pf))
       end.
End tuple.
Definition id_tuple_with_alt {A n}
  : forall (value value_for_alt : tuple A n), tuple A n
  := match n with
     | O => id_with_alt
     | S n' => id_tuple'_with_alt
     end.
Definition id_tuple_with_alt_proof {A n}
  : forall (value value_for_alt : tuple A n) {pf : value = value_for_alt},
    tuple A n
  := match n with
     | O => id_with_alt_proof
     | S n' => id_tuple'_with_alt_proof
     end.

Lemma unfold_id_with_alt {A} value value'
  : @id_with_alt A value value' = value.
Proof. reflexivity. Qed.
Lemma unfold_id_with_alt_proof {A} value value' pf
  : @id_with_alt_proof A value value' pf = value.
Proof. reflexivity. Qed.
Local Ltac t_ind n :=
  let IHn := fresh "IHn" in
  induction n as [|n IHn]; cbn [id_tuple'_with_alt id_tuple'_with_alt_proof];
  first [ rewrite unfold_id_with_alt | rewrite unfold_id_with_alt_proof ];
  [ reflexivity
  | rewrite IHn, <- surjective_pairing; reflexivity ].
Lemma unfold_id_tuple'_with_alt {A n} value value'
  : @id_tuple'_with_alt A n value value' = value.
Proof. t_ind n. Qed.
Lemma unfold_id_tuple'_with_alt_proof {A n} value value' pf
  : @id_tuple'_with_alt_proof A n value value' pf = value.
Proof. t_ind n. Qed.
Lemma unfold_id_tuple_with_alt {A n} value value'
  : @id_tuple_with_alt A n value value' = value.
Proof.
  destruct n; cbn [id_tuple_with_alt];
    [ apply unfold_id_with_alt | apply unfold_id_tuple'_with_alt ].
Qed.
Lemma unfold_id_tuple_with_alt_proof {A n} value value' pf
  : @id_tuple_with_alt_proof A n value value' pf = value.
Proof.
  destruct n; cbn [id_tuple_with_alt_proof];
    [ apply unfold_id_with_alt_proof | apply unfold_id_tuple'_with_alt_proof ].
Qed.
