Require Import Crypto.Reflection.Syntax.
Require Import Crypto.Reflection.Eta.
Require Import Crypto.Reflection.ExprInversion.
Require Import Crypto.Util.Tactics.BreakMatch.
Require Import Crypto.Util.Tactics.DestructHead.

Section language.
  Context {base_type_code : Type}
          {interp_base_type : base_type_code -> Type}
          {op : flat_type base_type_code -> flat_type base_type_code -> Type}
          {interp_op : forall src dst, op src dst -> interp_flat_type interp_base_type src -> interp_flat_type interp_base_type dst}.

  Local Notation exprf := (@exprf base_type_code op interp_base_type).

  Local Ltac t_step :=
    match goal with
    | _ => reflexivity
    | _ => progress simpl in *
    | _ => intro
    | _ => progress break_match
    | _ => progress destruct_head prod
    | _ => progress cbv [LetIn.Let_In]
    | [ H : _ |- _ ] => rewrite H
    | _ => progress autorewrite with core
    | [ H : forall A B x, ?f A B x = x, H' : context[?f _ _ _] |- _ ]
      => rewrite H in H'
    | _ => progress unfold interp_flat_type_eta, interp_flat_type_eta', exprf_eta, exprf_eta', expr_eta, expr_eta'
    end.
  Local Ltac t := repeat t_step.

  Section gen_flat_type.
    Context (eta : forall {A B}, A * B -> A * B)
            (eq_eta : forall A B x, @eta A B x = x).
    Lemma eq_interp_flat_type_eta_gen {var t T f} x
      : @interp_flat_type_eta_gen base_type_code var eta t T f x = f x.
    Proof. induction t; t. Qed.

    (* Local *) Hint Rewrite @eq_interp_flat_type_eta_gen.

    Section gen_type.
      Context (exprf_eta : forall {t} (e : exprf t), exprf t)
              (eq_interp_exprf_eta : forall t e, interpf (@interp_op) (@exprf_eta t e) = interpf (@interp_op) e).
      Lemma interp_expr_eta_gen {t e}
        : forall x,
          interp (@interp_op) (expr_eta_gen eta exprf_eta (t:=t) e) x = interp (@interp_op) e x.
      Proof. t. Qed.
    End gen_type.
    (* Local *) Hint Rewrite @interp_expr_eta_gen.

    Lemma interpf_exprf_eta_gen {t e}
      : interpf (@interp_op) (exprf_eta_gen eta (t:=t) e) = interpf (@interp_op) e.
    Proof. induction e; t. Qed.

    Lemma InterpExprEtaGen {t e}
      : forall x, Interp (@interp_op) (ExprEtaGen eta (t:=t) e) x = Interp (@interp_op) e x.
    Proof. apply interp_expr_eta_gen; intros; apply interpf_exprf_eta_gen. Qed.
  End gen_flat_type.
  (* Local *) Hint Rewrite @eq_interp_flat_type_eta_gen.
  (* Local *) Hint Rewrite @interp_expr_eta_gen.
  (* Local *) Hint Rewrite @interpf_exprf_eta_gen.

  Lemma eq_interp_flat_type_eta {var t T f} x
    : @interp_flat_type_eta base_type_code var t T f x = f x.
  Proof. t. Qed.
  (* Local *) Hint Rewrite @eq_interp_flat_type_eta.
  Lemma eq_interp_flat_type_eta' {var t T f} x
    : @interp_flat_type_eta' base_type_code var t T f x = f x.
  Proof. t. Qed.
  (* Local *) Hint Rewrite @eq_interp_flat_type_eta'.
  Lemma interpf_exprf_eta {t e}
    : interpf (@interp_op) (exprf_eta (t:=t) e) = interpf (@interp_op) e.
  Proof. t. Qed.
  (* Local *) Hint Rewrite @interpf_exprf_eta.
  Lemma interpf_exprf_eta' {t e}
    : interpf (@interp_op) (exprf_eta' (t:=t) e) = interpf (@interp_op) e.
  Proof. t. Qed.
  (* Local *) Hint Rewrite @interpf_exprf_eta'.
  Lemma interp_expr_eta {t e}
    : forall x, interp (@interp_op) (expr_eta (t:=t) e) x = interp (@interp_op) e x.
  Proof. t. Qed.
  Lemma interp_expr_eta' {t e}
    : forall x, interp (@interp_op) (expr_eta' (t:=t) e) x = interp (@interp_op) e x.
  Proof. t. Qed.
  Lemma InterpExprEta {t e}
    : forall x, Interp (@interp_op) (ExprEta (t:=t) e) x = Interp (@interp_op) e x.
  Proof. apply interp_expr_eta. Qed.
  Lemma InterpExprEta' {t e}
    : forall x, Interp (@interp_op) (ExprEta' (t:=t) e) x = Interp (@interp_op) e x.
  Proof. apply interp_expr_eta'. Qed.
  Lemma InterpExprEta_arrow {s d e}
    : forall x, Interp (t:=Arrow s d) (@interp_op) (ExprEta (t:=Arrow s d) e) x = Interp (@interp_op) e x.
  Proof. exact (@InterpExprEta (Arrow s d) e). Qed.
  Lemma InterpExprEta'_arrow {s d e}
    : forall x, Interp (t:=Arrow s d) (@interp_op) (ExprEta' (t:=Arrow s d) e) x = Interp (@interp_op) e x.
  Proof. exact (@InterpExprEta' (Arrow s d) e). Qed.

  Lemma eq_interp_eta {t e}
    : forall x, interp_eta interp_op (t:=t) e x = interp interp_op e x.
  Proof. apply eq_interp_flat_type_eta. Qed.
  Lemma eq_InterpEta {t e}
    : forall x, InterpEta interp_op (t:=t) e x = Interp interp_op e x.
  Proof. apply eq_interp_eta. Qed.
End language.

Hint Rewrite @eq_interp_flat_type_eta @eq_interp_flat_type_eta' @interpf_exprf_eta @interpf_exprf_eta' @interp_expr_eta @interp_expr_eta' @InterpExprEta @InterpExprEta' @InterpExprEta_arrow @InterpExprEta'_arrow @eq_interp_eta @eq_InterpEta : reflective_interp.
