Library MetaCoq.Erasure.EInduction

Require Import List ssreflect ssrbool.
From MetaCoq.Template Require Import utils BasicAst.
From MetaCoq.Erasure Require Import EAst EAstUtils.
From MetaCoq.PCUIC Require Import PCUICSize.
From Equations Require Import Equations.
Set Equations Transparent.

Deriving a compact induction principle for terms

Allows to get the right induction principle on lists of terms appearing in the term syntax (in evar, applications, branches of cases and (co-)fixpoints.
Custom induction principle on syntax, dealing with the various lists appearing in terms.

Lemma term_forall_list_ind :
   P : term Type,
    (P tBox)
    ( n : nat, P (tRel n))
    ( i : ident, P (tVar i))
    ( (n : nat) (l : list term), All P l P (tEvar n l))
    ( (n : name) (t : term), P t P (tLambda n t))
    ( (n : name) (t : term),
        P t t0 : term, P t0 P (tLetIn n t t0))
    ( t u : term, P t P u P (tApp t u))
    ( s, P (tConst s))
    ( (i : inductive) (n : nat) (args : list term),
      All P args P (tConstruct i n args))
    ( (p : inductive × nat) (t : term),
        P t l : list (list name × term),
        All (fun xP x.2) l P (tCase p t l))
    ( (s : projection) (t : term), P t P (tProj s t))
    ( (m : mfixpoint term) (n : nat), All (fun xP (dbody x)) m P (tFix m n))
    ( (m : mfixpoint term) (n : nat), All (fun xP (dbody x)) m P (tCoFix m n))
    
     t : term, P t.
Proof.
  intros until t. revert t.
  fix auxt 1.
  move auxt at top.
  destruct t; match goal with
                 H : _ |- _apply H
              end; auto.
  revert l.
  fix auxl' 1.
  destruct l; constructor; [|apply auxl'].
  apply auxt.
  revert l.
  fix auxl' 1.
  destruct l; constructor; [|apply auxl'].
  apply auxt.

  revert l.
  fix auxl' 1.
  destruct l; constructor; [|apply auxl'].
  apply auxt.

  revert m.
  fix auxm 1.
  destruct m; constructor; [|apply auxm].
  apply auxt.

  revert m.
  fix auxm 1.
  destruct m; constructor; [|apply auxm].
  apply auxt.
Defined.

Ltac applyhyp :=
  match goal with
    H : _ |- _apply H
  end.

Class Hyp (T : Type) := hyp : T.
#[global]
Hint Extern 10 (Hyp _) ⇒ exactly_once multimatch goal with H : _ |- _
exact H end : typeclass_instances.
Class AHyp (T : Type) := ahyp : T.
#[global]
Hint Extern 10 (AHyp _) ⇒ multimatch goal with H : _ |- _
eapply H; shelve end : typeclass_instances.

Ltac inv H :=
  inversion_clear H ||
  match H with
  | @hyp _ ?Xinversion_clear X
  | @ahyp _ ?Xinversion_clear X
  end.

Fixpoint size t : nat :=
  match t with
  | tRel i ⇒ 1
  | tEvar ev argsS (list_size size args)
  | tLambda na MS (size M)
  | tApp u vS (size u + size v)
  | tLetIn na b b'S (size b + size b')
  | tCase ind c brsS (size c + list_size (fun xsize x.2) brs)
  | tProj p cS (size c)
  | tFix mfix idxS (list_size (fun xsize (dbody x)) mfix)
  | tCoFix mfix idxS (list_size (fun xsize (dbody x)) mfix)
  | tConstruct _ _ ignore_argsS (list_size size ignore_args)
  | _ ⇒ 1
  end.

Lemma size_mkApps f l : size (mkApps f l) = size f + list_size size l.
Proof.
  induction l in f |- *; simpl; try lia.
  rewrite IHl. simpl. lia.
Qed.

Lemma decompose_app_rec_size t l :
  let da := decompose_app_rec t l in
  size da.1 + list_size size da.2 = size t + list_size size l.
Proof.
  induction t in l |- *; cbn; try lia.
  rewrite IHt1; cbn. lia.
Qed.

Lemma decompose_app_size t :
  let da := decompose_app t in
  size da.1 + list_size size da.2 = size t.
Proof.
  unfold decompose_app.
  rewrite (decompose_app_rec_size t []); cbn. lia.
Qed.

Local Lemma decompose_app_rec_inv {t l' f l} :
  decompose_app_rec t l' = (f, l)
  mkApps t l' = mkApps f l.
Proof.
  induction t in f, l', l |- *; try intros [= <- <-]; try reflexivity.
  simpl. apply/IHt1.
Defined.

Local Lemma decompose_app_inv {t f l} :
  decompose_app t = (f, l) t = mkApps f l.
Proof. by apply/decompose_app_rec_inv. Defined.

Local Lemma decompose_app_rec_notApp :
   t l u l',
    decompose_app_rec t l = (u, l')
    ~~ isApp u.
Proof.
  intros t l u l' e.
  induction t in l, u, l', e |- ×.
  all: try (cbn in e ; inversion e ; reflexivity).
  cbn in e. eapply IHt1. eassumption.
Defined.

Local Lemma decompose_app_notApp :
   t u l,
    decompose_app t = (u, l)
    ~~ isApp u.
Proof.
  intros t u l e.
  eapply decompose_app_rec_notApp. eassumption.
Defined.

Local Lemma decompose_app_app t u f l : decompose_app (tApp t u) = (f, l) l [].
Proof.
  intros da.
  pose proof (decompose_app_inv da).
  intros →. cbn in H. subst f.
  now move: (decompose_app_notApp _ _ _ da).
Defined.

Lemma size_mkApps_f {f l} (Hf : ~~ isApp f) (Hl : l []) : size f < size (mkApps f l).
Proof.
  rewrite size_mkApps.
  induction l; cbn; congruence || lia.
Qed.

Lemma size_mkApps_l {f l} (Hf : ~~ isApp f) (Hl : l []) : list_size size l < size (mkApps f l).
Proof.
  rewrite size_mkApps.
  destruct f ⇒ /= //; try lia.
Qed.

Custom induction principle on syntax, dealing with the various lists appearing in terms.

Section All_rec.
  Context (P : term Type).
  Context {A} (proj : A term).

  Equations? All_rec (l : list A) (auxt : y, size y < (list_size (fun xsize (proj x)) l) P y) :
    All (fun xP (proj x)) l :=
    All_rec [] auxt := All_nil;
    All_rec (x :: xs) auxt := All_cons (auxt (proj x) _) (All_rec xs (fun y Hauxt y _)).
  Proof.
    all:lia.
  Qed.
End All_rec.

Global Instance Wf_size_lt : WellFounded (MR lt size) := _.

Module MkAppsInd.
Section MkApps_rec.
  Context {P : term Type}.

  Context (pbox : P tBox)
    (prel : n : nat, P (tRel n))
    (pvar : i : ident, P (tVar i))
    (pevar : (n : nat) (l : list term), All P l P (tEvar n l))
    (plam : (n : name) (t : term), P t P (tLambda n t))
    (plet : (n : name) (t : term),
        P t t0 : term, P t0 P (tLetIn n t t0))
    (papp : t u,
      ~~ isApp t u nil P t All P u P (mkApps t u))
    (pconst : s, P (tConst s))
    (pconstruct : (i : inductive) (n : nat) args, All P args P (tConstruct i n args))
    (pcase : (p : inductive × nat) (t : term),
        P t l : list (list name × term),
        All (fun xP x.2) l P (tCase p t l))
    (pproj : (s : projection) (t : term), P t P (tProj s t))
    (pfix : (m : mfixpoint term) (n : nat), All (fun xP (dbody x)) m P (tFix m n))
    (pcofix : (m : mfixpoint term) (n : nat), All (fun xP (dbody x)) m P (tCoFix m n)).

  Definition inspect {A} (x : A) : { y : A | x = y } := exist _ x eq_refl.

  Import EqNotations.

  Equations? rec (t : term) : P t by wf t (MR lt size) :=
    | tRel nprel n
    | tVar npvar n
    | tEvar n lpevar n l (All_rec P (fun xx) l (fun x Hrec x))
    | tBoxpbox
    | tLambda n1 tplam n1 t (rec t)
    | tLetIn n2 t0 t1plet n2 t0 (rec t0) t1 (rec t1)
    | tApp t2 t3 with inspect (decompose_app (tApp t2 t3)) :=
      { | exist _ (t, l) da :=
        let napp := decompose_app_notApp _ _ _ da in
        let nonnil := decompose_app_app _ _ _ _ da in
        let pt := rec t in
        let pl := All_rec P id l (fun x Hrec x) in
        rew _ in papp t l napp nonnil pt pl }
    | tConst kpconst k
    | tConstruct i n argspconstruct i n _ (All_rec P id args (fun x Hrec x))
    | tCase ina c brspcase ina c (rec c) brs (All_rec P (fun xx.2) brs (fun x Hrec x))
    | tProj p cpproj p c (rec c)
    | tFix mfix idxpfix mfix idx (All_rec P dbody mfix (fun x Hrec x))
    | tCoFix mfix idxpcofix mfix idx (All_rec P dbody mfix (fun x Hrec x)).
  Proof.
    all:unfold MR; cbn; auto with arith. 4:lia.
    - clear -napp nonnil da rec.
      pose proof (decompose_app_size (tApp t2 t3)).
      rewrite da in H. cbn in H. rewrite <- H.
      abstract (destruct l; try congruence; cbn; lia).
    - clear -da rec H.
      pose proof (decompose_app_size (tApp t2 t3)).
      rewrite da in H0. cbn in H0. rewrite <- H0.
      unfold id in H. change (fun xsize x) with size in H. abstract lia.
    - clear -da. abstract (eapply decompose_app_inv in da; now symmetry).
  Qed.

  End MkApps_rec.

  Section MkApps_case.
  Context {P : term Type}.

  Context (pbox : P tBox)
    (prel : n : nat, P (tRel n))
    (pvar : i : ident, P (tVar i))
    (pevar : (n : nat) (l : list term), P (tEvar n l))
    (plam : (n : name) (t : term), P (tLambda n t))
    (plet : (n : name) (t : term), t0 : term, P (tLetIn n t t0))
    (papp : t u, ~~ isApp t u nil P (mkApps t u))
    (pconst : s, P (tConst s))
    (pconstruct : (i : inductive) (n : nat) args, P (tConstruct i n args))
    (pcase : (p : inductive × nat) (t : term) (l : list (list name × term)), P (tCase p t l))
    (pproj : (s : projection) (t : term), P (tProj s t))
    (pfix : (m : mfixpoint term) (n : nat), P (tFix m n))
    (pcofix : (m : mfixpoint term) (n : nat), P (tCoFix m n)).

  Import EqNotations.

  Equations case (t : term) : P t :=
    | tRel nprel n
    | tVar npvar n
    | tEvar n lpevar n l
    | tBoxpbox
    | tLambda n1 tplam n1 t
    | tLetIn n2 t0 t1plet n2 t0 t1
    | tApp t2 t3 with inspect (decompose_app (tApp t2 t3)) :=
      { | exist _ (t, l) da :=
        let napp := decompose_app_notApp _ _ _ da in
        let nonnil := decompose_app_app _ _ _ _ da in
        rew [P] (eq_sym (decompose_app_inv da)) in papp t l napp nonnil }
    | tConst kpconst k
    | tConstruct i n argspconstruct i n args
    | tCase ina c brspcase ina c brs
    | tProj p cpproj p c
    | tFix mfix idxpfix mfix idx
    | tCoFix mfix idxpcofix mfix idx.

  End MkApps_case.

End MkAppsInd.