Library MetaCoq.PCUIC.PCUICPosition






Inductive choice :=
| app_l
| app_r
| case_c
| proj_c
| lam_ty
| lam_tm
| prod_l
| prod_r

| let_in.


Instance reflect_choice : ReflectEq choice :=
  let h := EqDec_ReflectEq choice in _.

Definition position := list choice.

Fixpoint validpos t (p : position) {struct p} :=
  match p with
  | []true
  | c :: p
    match c, t with
    | app_l, tApp u vvalidpos u p
    | app_r, tApp u vvalidpos v p
    | case_c, tCase indn pr c brsvalidpos c p
    | proj_c, tProj pr cvalidpos c p
    | lam_ty, tLambda na A tvalidpos A p
    | lam_tm, tLambda na A tvalidpos t p
    | prod_l, tProd na A Bvalidpos A p
    | prod_r, tProd na A Bvalidpos B p
    
    | let_in, tLetIn na A b tvalidpos t p
    | _, _false
    end
  end.

Definition pos (t : term) := { p : position | validpos t p = true }.


Definition dapp_l u v (p : pos u) : pos (tApp u v) :=
  exist (app_l :: ` p) (proj2_sig p).

Definition dapp_r u v (p : pos v) : pos (tApp u v) :=
  exist (app_r :: ` p) (proj2_sig p).

Definition dcase_c indn pr c brs (p : pos c) : pos (tCase indn pr c brs) :=
  exist (case_c :: ` p) (proj2_sig p).

Definition dproj_c pr c (p : pos c) : pos (tProj pr c) :=
  exist (proj_c :: ` p) (proj2_sig p).

Definition dlam_ty na A t (p : pos A) : pos (tLambda na A t) :=
  exist (lam_ty :: ` p) (proj2_sig p).

Definition dlam_tm na A t (p : pos t) : pos (tLambda na A t) :=
  exist (lam_tm :: ` p) (proj2_sig p).

Definition dprod_l na A B (p : pos A) : pos (tProd na A B) :=
  exist (prod_l :: ` p) (proj2_sig p).

Definition dprod_r na A B (p : pos B) : pos (tProd na A B) :=
  exist (prod_r :: ` p) (proj2_sig p).

Definition dlet_in na A b t (p : pos t) : pos (tLetIn na A b t) :=
  exist (let_in :: ` p) (proj2_sig p).

Lemma eq_term_upto_valid_pos :
   {u v p Re Rle},
    validpos u p
    eq_term_upto_univ Re Rle u v
    validpos v p.

Lemma eq_term_valid_pos :
   `{cf : checker_flags} {G u v p},
    validpos u p
    eq_term G u v
    validpos v p.

Inductive positionR : position position Prop :=
| positionR_app_lr p q : positionR (app_r :: p) (app_l :: q)
| positionR_deep c p q : positionR p q positionR (c :: p) (c :: q)
| positionR_root c p : positionR (c :: p) [].


Definition posR {t} (p q : pos t) : Prop :=
  positionR (` p) (` q).

Lemma posR_Acc :
   t p, Acc (@posR t) p.

Fixpoint atpos t (p : position) {struct p} : term :=
  match p with
  | []t
  | c :: p
    match c, t with
    | app_l, tApp u vatpos u p
    | app_r, tApp u vatpos v p
    | case_c, tCase indn pr c brsatpos c p
    | proj_c, tProj pr catpos c p
    | lam_ty, tLambda na A tatpos A p
    | lam_tm, tLambda na A tatpos t p
    | prod_l, tProd na A Batpos A p
    | prod_r, tProd na A Batpos B p
    | let_in, tLetIn na A b tatpos t p
    | _, _tRel 0
    end
  end.

Lemma poscat_atpos :
   t p q, atpos t (p ++ q) = atpos (atpos t p) q.

Lemma poscat_valid :
   t p q,
    validpos t p
    validpos (atpos t p) q
    validpos t (p ++ q).

Lemma positionR_poscat :
   p q1 q2,
    positionR q1 q2
    positionR (p ++ q1) (p ++ q2).

Lemma atpos_assoc :
   t p q,
    atpos (atpos t p) q = atpos t (p ++ q).

Definition transitive {A} (R : A A Prop) :=
   u v w, R u v R v w R u w.

Lemma positionR_trans : transitive positionR.

Lemma posR_trans :
   t, transitive (@posR t).

Lemma positionR_poscat_nonil :
   p q, q [] positionR (p ++ q) p.

Inductive stack : Type :=
| Empty
| App (t : term) (π : stack)
| Fix (f : mfixpoint term) (n : nat) (args : list term) (π : stack)
| CoFix (f : mfixpoint term) (n : nat) (args : list term) (π : stack)
| Case (indn : inductive × nat) (p : term) (brs : list (nat × term)) (π : stack)
| Proj (p : projection) (π : stack)
| Prod_l (na : name) (B : term) (π : stack)
| Prod_r (na : name) (A : term) (π : stack)
| Lambda_ty (na : name) (b : term) (π : stack)
| Lambda_tm (na : name) (A : term) (π : stack)
| coApp (t : term) (π : stack).

Notation "'ε'" := (Empty).


Instance reflect_stack : ReflectEq stack :=
  let h := EqDec_ReflectEq stack in _.

Fixpoint zipc t stack :=
  match stack with
  | εt
  | App u π ⇒ zipc (tApp t u) π
  | Fix f n args π ⇒ zipc (tApp (mkApps (tFix f n) args) t) π
  | CoFix f n args π ⇒ zipc (tApp (mkApps (tCoFix f n) args) t) π
  | Case indn pred brs π ⇒ zipc (tCase indn pred t brs) π
  | Proj p π ⇒ zipc (tProj p t) π
  | Prod_l na B π ⇒ zipc (tProd na t B) π
  | Prod_r na A π ⇒ zipc (tProd na A t) π
  | Lambda_ty na b π ⇒ zipc (tLambda na t b) π
  | Lambda_tm na A π ⇒ zipc (tLambda na A t) π
  | coApp u π ⇒ zipc (tApp u t) π
  end.

Definition zip (t : term × stack) := zipc (fst t) (snd t).

Tactic Notation "zip" "fold" "in" hyp(h) :=
  lazymatch type of h with
  | context C[ zipc ?t ?π ] ⇒
    let C' := context C[ zip (t,π) ] in
    change C' in h
  end.

Tactic Notation "zip" "fold" :=
  lazymatch goal with
  | |- context C[ zipc ?t ?π ] ⇒
    let C' := context C[ zip (t,π) ] in
    change C'
  end.

Fixpoint decompose_stack π :=
  match π with
  | App u π ⇒ let '(l,π) := decompose_stack π in (u :: l, π)
  | _([], π)
  end.

Fixpoint appstack l π :=
  match l with
  | u :: lApp u (appstack l π)
  | []π
  end.

Lemma decompose_stack_eq :
   π l ρ,
    decompose_stack π = (l, ρ)
    π = appstack l ρ.

Lemma decompose_stack_not_app :
   π l u ρ,
    decompose_stack π = (l, App u ρ) False.

Lemma zipc_appstack :
   {t args ρ},
    zipc t (appstack args ρ) = zipc (mkApps t args) ρ.

Lemma decompose_stack_appstack :
   l ρ,
    decompose_stack (appstack l ρ) =
    (l ++ fst (decompose_stack ρ), snd (decompose_stack ρ)).

Fixpoint decompose_stack_at π n : option (list term × term × stack) :=
  match π with
  | App u π ⇒
    match n with
    | 0 ⇒ ret ([], u, π)
    | S n
      r <- decompose_stack_at π n ;;
        let '(l, v, π) := r in
        ret (u :: l, v, π)
    end
  | _None
  end.

Lemma decompose_stack_at_eq :
   π n l u ρ,
    decompose_stack_at π n = Some (l,u,ρ)
    π = appstack l (App u ρ).

Lemma decompose_stack_at_length :
   π n l u ρ,
    decompose_stack_at π n = Some (l,u,ρ)
    #|l| = n.

Fixpoint stack_context π : context :=
  match π with
  | ε[]
  | App u π ⇒ stack_context π
  | Fix f n args π ⇒ stack_context π
  | CoFix f n args π ⇒ stack_context π
  | Case indn pred brs π ⇒ stack_context π
  | Proj p π ⇒ stack_context π
  | Prod_l na B π ⇒ stack_context π
  | Prod_r na A π ⇒ stack_context π ,, vass na A
  | Lambda_ty na u π ⇒ stack_context π
  | Lambda_tm na A π ⇒ stack_context π ,, vass na A
  | coApp u π ⇒ stack_context π
  end.

Lemma stack_context_appstack :
  args},
    stack_context (appstack args π) = stack_context π.

Fixpoint stack_position π : position :=
  match π with
  | ε[]
  | App u ρ ⇒ stack_position ρ ++ [ app_l ]
  | Fix f n args ρ ⇒ stack_position ρ ++ [ app_r ]
  | CoFix f n args ρ ⇒ stack_position ρ ++ [ app_r ]
  | Case indn pred brs ρ ⇒ stack_position ρ ++ [ case_c ]
  | Proj pr ρ ⇒ stack_position ρ ++ [ proj_c ]
  | Prod_l na B ρ ⇒ stack_position ρ ++ [ prod_l ]
  | Prod_r na A ρ ⇒ stack_position ρ ++ [ prod_r ]
  | Lambda_ty na u ρ ⇒ stack_position ρ ++ [ lam_ty ]
  | Lambda_tm na A ρ ⇒ stack_position ρ ++ [ lam_tm ]
  | coApp u ρ ⇒ stack_position ρ ++ [ app_r ]
  end.

Lemma stack_position_atpos :
   t π, atpos (zipc t π) (stack_position π) = t.

Lemma stack_position_valid :
   t π, validpos (zipc t π) (stack_position π).

Definition stack_pos t π : pos (zipc t π) :=
  exist (stack_position π) (stack_position_valid t π).

Fixpoint list_make {A} n x : list A :=
  match n with
  | 0 ⇒ []
  | S nx :: list_make n x
  end.

Lemma list_make_app_r :
   A n (x : A),
    x :: list_make n x = list_make n x ++ [x].

Lemma stack_position_appstack :
   args ρ,
    stack_position (appstack args ρ) =
    stack_position ρ ++ list_make #|args| app_l.

Section Stacks.

  Context (Σ : global_env_ext).
  Context `{checker_flags}.

  Lemma red1_context :
     Γ t u π,
      red1 Σ (Γ ,,, stack_context π) t u
      red1 Σ Γ (zip (t, π)) (zip (u, π)).

  Corollary red_context :
     Γ t u π,
      red Σ (Γ ,,, stack_context π) t u
      red Σ Γ (zip (t, π)) (zip (u, π)).

  Lemma zipc_inj :
     u v π, zipc u π = zipc v π u = v.

  Definition isStackApp π :=
    match π with
    | App _ _true
    | _false
    end.

  Lemma isStackApp_false_appstack :
     l π,
      isStackApp (appstack l π) = false
      l = [].

  Definition zipx (Γ : context) (t : term) (π : stack) : term :=
    it_mkLambda_or_LetIn Γ (zipc t π).

  Fixpoint context_position Γ : position :=
    match Γ with
    | [][]
    | {| decl_name := na ; decl_body := None ; decl_type := A |} :: Γ
      context_position Γ ++ [ lam_tm ]
    | {| decl_name := na ; decl_body := Some b ; decl_type := A |} :: Γ
      context_position Γ ++ [ let_in ]
    end.

  Lemma context_position_atpos :
     Γ t, atpos (it_mkLambda_or_LetIn Γ t) (context_position Γ) = t.

  Lemma context_position_valid :
     Γ t, validpos (it_mkLambda_or_LetIn Γ t) (context_position Γ).

  Lemma positionR_context_position_inv :
     Γ p q,
      positionR (context_position Γ ++ p) (context_position Γ ++ q)
      positionR p q.

  Definition xposition Γ π : position :=
    context_position Γ ++ stack_position π.

  Lemma xposition_atpos :
     Γ t π, atpos (zipx Γ t π) (xposition Γ π) = t.

  Lemma xposition_valid :
     Γ t π, validpos (zipx Γ t π) (xposition Γ π).

  Lemma positionR_xposition_inv :
     Γ ρ1 ρ2,
      positionR (xposition Γ ρ1) (xposition Γ ρ2)
      positionR (stack_position ρ1) (stack_position ρ2).

  Definition xpos Γ t π : pos (zipx Γ t π) :=
    exist (xposition Γ π) (xposition_valid Γ t π).

  Lemma positionR_stack_pos_xpos :
     Γ π1 π2,
      positionR (stack_position π1) (stack_position π2)
      positionR (xposition Γ π1) (xposition Γ π2).

  Definition zipp t π :=
    let '(args, ρ) := decompose_stack π in
    mkApps t args.

  Fixpoint stack_catθ : stack) : stack :=
    match ρ with
    | Emptyθ
    | App u ρ ⇒ App u (stack_cat ρ θ)
    | Fix f n args ρ ⇒ Fix f n args (stack_cat ρ θ)
    | CoFix f n args ρ ⇒ CoFix f n args (stack_cat ρ θ)
    | Case indn p brs ρ ⇒ Case indn p brs (stack_cat ρ θ)
    | Proj p ρ ⇒ Proj p (stack_cat ρ θ)
    | Prod_l na B ρ ⇒ Prod_l na B (stack_cat ρ θ)
    | Prod_r na A ρ ⇒ Prod_r na A (stack_cat ρ θ)
    | Lambda_ty na u ρ ⇒ Lambda_ty na u (stack_cat ρ θ)
    | Lambda_tm na A ρ ⇒ Lambda_tm na A (stack_cat ρ θ)
    | coApp u ρ ⇒ coApp u (stack_cat ρ θ)
    end.

  Notation "ρ +++ θ" := (stack_cat ρ θ) (at level 20).

  Lemma stack_cat_appstack :
     args ρ,
      appstack args ε +++ ρ = appstack args ρ.

  Lemma decompose_stack_twice :
     π args ρ,
      decompose_stack π = (args, ρ)
      decompose_stack ρ = ([], ρ).

  Lemma zipc_stack_cat :
     t π ρ,
      zipc t (π +++ ρ) = zipc (zipc t π) ρ.

  Lemma stack_cat_empty :
     ρ, ρ +++ ε = ρ.

  Lemma stack_position_stack_cat :
     π ρ,
      stack_position (ρ +++ π) =
      stack_position π ++ stack_position ρ.

  Lemma stack_context_stack_cat :
     π ρ,
      stack_context (ρ +++ π) = stack_context π ,,, stack_context ρ.

  Lemma red1_zipp :
     Γ t u π,
      red1 Σ Γ t u
      red1 Σ Γ (zipp t π) (zipp u π).

  Lemma red_zipp :
     Γ t u π,
      red Σ Γ t u
      red Σ Γ (zipp t π) (zipp u π).

  Definition zippx t π :=
    let '(args, ρ) := decompose_stack π in
    it_mkLambda_or_LetIn (stack_context ρ) (mkApps t args).


  Lemma red1_zippx :
     Γ t u π,
      red1 Σ (Γ ,,, stack_context π) t u
      red1 Σ Γ (zippx t π) (zippx u π).

  Corollary red_zippx :
     Γ t u π,
      red Σ (Γ ,,, stack_context π) t u
      red Σ Γ (zippx t π) (zippx u π).

End Stacks.

Notation "ρ +++ θ" := (stack_cat ρ θ) (at level 20).