Set Implicit Arguments.
Require Import Morphisms Lia List.
From Undecidability.HOU Require Import calculus.calculus.
Import ListNotations.

Set Default Proof Using "Type".


Section Constants.

  Section ConstantsOfTerm.
    Context {X: Const}.
    Implicit Types (s t: exp X).

    Fixpoint consts s :=
      match s with
      | var x nil
      | const c [c]
      | s consts s
      | app s t consts s consts t
      end.
    Definition Consts S := (flat_map consts S).

    Lemma Consts_consts s S:
      s S consts s Consts S.
    Proof.
      unfold Consts; eauto using flat_map_in_incl.
    Qed.


    Lemma Consts_montone S T:
      S T Consts S Consts T.
    Proof.
      unfold Consts; eauto using flat_map_incl.
    Qed.


    Lemma consts_ren delta s:
      consts (ren s) = consts s.
    Proof.
      induction s in |-*; cbn; intuition; congruence.
    Qed.


    Lemma vars_subst_consts x s sigma:
      x vars s consts ( x) consts ( s).
    Proof.
      intros H % vars_varof; induction H in |-*; cbn; intuition.
      rewrite IHvarof; cbn; unfold funcomp; now rewrite consts_ren.
    Qed.


    Lemma consts_subst_in x sigma s:
      x consts ( s) x consts s
                            y, y vars s x consts ( y).
    Proof.
      induction s in |-*.
      - right; exists f; intuition.
      - cbn; intuition.
      - intros H % IHs; cbn -[vars]; intuition.
        destruct as [[]]; cbn -[vars] in *; intuition.
        right. exists n. intuition.
        unfold funcomp in ; now rewrite consts_ren in .
      - cbn; simplify; intuition.
        + specialize ( _ ); intuition.
          destruct H as [y]; right; exists y; intuition.
        + specialize ( _ ); intuition.
          destruct H as [y]; right; exists y; intuition.
    Qed.


    Lemma consts_subset_step s t:
      s > t consts t consts s.
    Proof.
      induction 1; cbn; intuition.
      subst. unfold .
      intros x ? % consts_subst_in; simplify; intuition.
      destruct H as [[|y] ?]; cbn in *; intuition.
    Qed.


    Lemma consts_subset_steps s t:
      s >* t consts t consts s.
    Proof.
      induction 1; firstorder using consts_subset_step.
    Qed.


    Lemma consts_subst_vars sigma s:
      consts ( s) consts s Consts (map (vars s)).
    Proof.
      intros x [|[y]] % consts_subst_in; simplify; intuition.
      right; eapply Consts_consts; eauto using in_map.
    Qed.


    Lemma consts_Lam k s:
      consts (Lambda k s) consts s.
    Proof.
      induction k; cbn; intuition.
    Qed.


    Lemma consts_AppL S t:
      consts (AppL S t) Consts S consts t.
    Proof.
      induction S; cbn; intuition.
      rewrite IHS; now rewrite app_assoc.
    Qed.


    Lemma consts_AppR s T:
      consts (AppR s T) consts s Consts T.
    Proof.
      induction T; cbn; intuition.
      rewrite IHT. intuition.
      split; intros c; simplify; intuition.
    Qed.


  End ConstantsOfTerm.

  Section ConstantSubstitution.

    Implicit Types (X Y Z : Const).

    Fixpoint subst_consts {X Y} (kappa: X exp Y) s :=
      match s with
      | var x var x
      | const c c
      | s (subst_consts ( >> ren shift) s)
      | app s t (subst_consts s) (subst_consts t)
      end.

    Lemma ren_subst_consts_commute X Y (zeta: X exp Y) delta s:
      subst_consts ( >> ren ) (ren s) = ren (subst_consts s).
    Proof.
      induction s in , |-*; cbn; eauto.
      - f_equal. rewrite IHs. f_equal.
        asimpl. reflexivity.
      - now rewrite , .
    Qed.


    Lemma subst_consts_comp X Y Z (zeta: X exp Y) (kappa: Y exp Z) s:
      subst_consts (subst_consts s) =
      subst_consts ( >> subst_consts ) s.
    Proof.
      induction s in , |-*; cbn; eauto.
      - f_equal. rewrite IHs. f_equal. fext.
        unfold funcomp at 4; unfold funcomp at 4.
        intros; rewrite ren_subst_consts_commute.
        reflexivity.
      - now rewrite , .
    Qed.


    Lemma subst_consts_ident Y zeta s:
      ( x: Y, x consts s x = const x) subst_consts s = s.
    Proof.
      intros; induction s in , H |-*; cbn; eauto.
      eapply H; cbn; eauto.
      rewrite IHs; eauto.
      unfold funcomp; now intros x % H.
      rewrite , ; eauto.
      all: intros; apply H; cbn; simplify; intuition.
    Qed.


    Lemma subst_const_comm {X Y} (zeta: X exp Y) sigma delta s:
      ( x, ( x) = var x)
      subst_consts ( s) = ( >> subst_consts ) (subst_consts ( >> ren ) s).
    Proof.
      induction s in , , |-*; intros H; cbn.
      - reflexivity.
      - unfold funcomp; asimpl.
        rewrite idSubst_exp; eauto.
        intros y; unfold funcomp; cbn.
        rewrite H; reflexivity.
      - f_equal. erewrite IHs with ( := 0 .: >> shift).
        2: intros []; cbn; unfold funcomp; eauto; rewrite H; reflexivity.
        f_equal; [| now asimpl].
        fext; intros []; cbn; eauto.
        unfold funcomp at 2.
        now rewrite ren_subst_consts_commute.
      - erewrite , ; eauto.
    Qed.


    Global Instance step_subst_consts X Y:
      Proper (Logic.eq ++> step ++> step) (@subst_consts X Y).
    Proof.
      intros ? s t H; induction H in |-*; cbn; eauto.
      econstructor; subst; unfold .
      erewrite subst_const_comm with ( := shift).
      f_equal. fext.
      all: intros []; cbn; eauto.
    Qed.


    Global Instance steps_subst_consts X Y:
      Proper (Logic.eq ++> star step ++> star step) (@subst_consts X Y).
    Proof.
      intros ? s t H; induction H in |-*; cbn; eauto; rewrite H; eauto.
    Qed.


    Global Instance equiv_subst_consts X Y:
      Proper (Logic.eq ++> equiv step ++> equiv step) (@subst_consts X Y).
    Proof.
      intros ? s t [v [ ]] % church_rosser; eauto;
        now rewrite , .
    Qed.


    Lemma subst_consts_consts X Y (zeta: X exp Y) (s: exp X):
      consts (subst_consts s) Consts (map (consts s)).
    Proof.
      unfold Consts; induction s in |-*; cbn; simplify; intuition.
      - rewrite IHs.
        unfold funcomp; rewrite map_map, !flat_map_concat_map, map_map.
        erewrite map_ext with (g := consts); intuition.
        now rewrite consts_ren.
      - rewrite , , !flat_map_concat_map; simplify.
        now rewrite concat_app.
    Qed.


    Lemma consts_in_subst_consts X Y (kappa: X exp Y) c s:
      c consts (subst_consts s) d, d consts s c consts ( d).
    Proof.
      rewrite subst_consts_consts.
      unfold Consts; rewrite in_flat_map; intros [e []]; mapinj.
      exists x; intuition.
    Qed.


    Lemma subst_consts_up Y Z (zeta: Y exp Z) (sigma: fin exp Y):
      up ( >> subst_consts ) = up >> subst_consts ( >> ren shift).
    Proof.
      fext; intros []; cbn; eauto.
      unfold funcomp at 1 2.
      now rewrite ren_subst_consts_commute.
    Qed.


    Lemma subst_const_comm_id Y zeta sigma (s: exp Y):
      subst_consts s = s
      ( >> subst_consts ) s = subst_consts ( s).
    Proof.
      induction s in , |-*; cbn; eauto.
      - injection 1 as H. f_equal.
        rewrite IHs; eauto.
        now rewrite subst_consts_up.
      - injection 1 as H. f_equal; eauto.
    Qed.


    Lemma typing_constants X n Gamma s A :
       ⊢(n) s : A c, c consts s ord (ctype X c) S n.
    Proof.
      induction 1; cbn; intuition; subst; eauto.
      simplify in ; intuition.
    Qed.


    Lemma typing_Consts X c n Gamma S' L:
       ⊢₊(n) S' : L c Consts S' ord (ctype X c) S n.
    Proof.
      induction 1; cbn; simplify; intuition eauto using typing_constants.
    Qed.


    Lemma preservation_consts X Y Gamma s A (zeta: X exp Y):
       s : A ( x, x consts s x : ctype X x)
       subst_consts s : A.
    Proof.
      induction 1 in |-*; cbn; eauto.
      - intros H'. econstructor. eapply IHtyping.
        intros; eapply preservation_under_renaming; eauto.
        intros ?; cbn; eauto.
      - intros H'. econstructor.
        eapply ; intros ??; eapply H'; simplify; intuition.
        eapply ; intros ??; eapply H'; simplify; intuition.
    Qed.


    Lemma ordertyping_preservation_consts X Y n Gamma s A (zeta: X exp Y):
       ⊢(n) s : A ( x, x consts s ⊢(n) x : ctype X x)
       ⊢(n) subst_consts s : A.
    Proof.
      induction 1 in |-*; cbn; eauto.
      - intros H'. econstructor. eapply IHordertyping.
        intros; eapply ordertyping_preservation_under_renaming; eauto.
        intros ?; cbn; eauto.
      - intros H'. econstructor.
        eapply ; intros ??; eapply H'; simplify; intuition.
        eapply ; intros ??; eapply H'; simplify; intuition.
    Qed.


    Lemma subst_consts_Lambda Y Z (zeta: Y exp Z) k s:
        subst_consts (Lambda k s) = Lambda k (subst_consts ( >> ren (plus k)) s).
    Proof.
        induction k in |-*; cbn; asimpl; eauto.
        f_equal. rewrite IHk. f_equal. f_equal.
        asimpl. fext; intros x; unfold funcomp; f_equal; fext; intros ?.
        unfold shift; simplify; f_equal; .
    Qed.


    Lemma subst_consts_AppL X Y (tau: X exp Y) S t:
        subst_consts (AppL S t) = AppL (map (subst_consts ) S) (subst_consts t).
    Proof.
        induction S; cbn; congruence.
    Qed.


    Lemma subst_consts_AppR X Y (tau: X exp Y) s T:
        subst_consts (AppR s T) = AppR (subst_consts s) (map (subst_consts ) T).
    Proof.
        induction T; cbn; congruence.
    Qed.


  End ConstantSubstitution.
End Constants.