Library LambdaJ_ndr


Require Import Metatheory.
Require Import LambdaES_Defs.
Require Import LambdaES_Infrastructure.
Require Import LambdaES_Tactics.
Require Import LambdaES_occ.
Require Import LambdaES_fv.
Require Import Arith.

Definitions


ndr_n t x y t' n means that t' is obtained by replacing exactly n times the variable x by the variable y in t
Inductive ndr_n : pterm -> var -> var -> pterm -> nat -> Prop :=
  | ndr_bvar : forall x y z, ndr_n (pterm_bvar z) x y (pterm_bvar z) 0
  | ndr_varnone : forall x y z, ndr_n (pterm_fvar z) x y (pterm_fvar z) 0
  | ndr_varsome : forall x y, ndr_n (pterm_fvar x) x y (pterm_fvar y) 1
  | ndr_abs : forall t t' x y n, ndr_n t x y t' n ->
                        ndr_n(pterm_abs t) x y (pterm_abs t') n
  | ndr_app : forall t t' u u' x y n n' n'', n'' = n + n' ->
                        ndr_n t x y u n -> ndr_n t' x y u' n' ->
                        ndr_n (pterm_app t t') x y (pterm_app u u') n''
  | ndr_sub : forall t t' x y u u' n n' n'', n'' = n + n' ->
                        ndr_n (t) x y (t') n -> ndr_n u x y u' n' ->
                        ndr_n (pterm_sub t u) x y (pterm_sub t' u') n''.
Hint Constructors ndr_n.

 Definition ndr t x y t' := exists n, (1 <= n) /\ (n < fv_occ x t) /\ ndr_n t x y t' n.

Tactics using definitions


Ltac ndr_elim H :=
  match type of H with
  | ndr ?t ?x ?y ?t' =>
      let n := fresh "n" in
      let H1 := fresh H in
      let H2 := fresh H in
      let H3 := fresh H in
      inversion H as [n H1] ; destruct H1 as [H1 H2] ; destruct H2 as [H2 H3]
end.

Ltac deep_elim_ndr H :=
  match type of H with
  | ndr ?t ?x ?y ?t' =>
      let n := fresh "n" in
      let H1 := fresh H in
      let H2 := fresh H in
      let H3 := fresh H in
      inversion H as [n H1] ; destruct H1 as [H1 H2] ; destruct H2 as [H2 H3] ; inversion H3 ; intros ; subst
end.

Lemmas


About ndr

Lemma ndr_n_refl : forall t x y, ndr_n t x y t 0.
Proof.
  intro t. assert (0 = 0 + 0) as H by omega.
  basic_pterm_induction t using (rewrite H ; auto).
  rewrite H. apply ndr_app with (n:=0) (n':=0) ; auto.
  rewrite H. apply ndr_sub with (n:=0) (n':=0) ; auto.
Qed.
Hint Resolve ndr_n_refl.

Lemma ndr_n_opens : forall t x y t' n u, ndr_n t x y t' n -> ndr_n (t^^u) x y (t'^^u) n.
Proof. intros. unfold open. generalize 0. induction H ; simpl ; eauto. Qed.

Lemma ndr_n_opens_gen : forall t x y t' n u k, ndr_n t x y t' n -> ndr_n ({k~>u}t) x y ({k~>u}t') n.
Proof. intros. unfold open. gen k. induction H ; intros ; simpl ; eauto. Qed.

Lemma ndr_n_preservation_term : forall t, term t -> (forall t' x y n, ndr_n t x y t' n -> term t').
Proof.
  intros t H0. induction H0 using term_size_induction ; intros t' x' y' n Hn ; inversion Hn ; subst ; auto.
    destruct H. apply term_abs with (L := x \u (fv t1)). intros.
    apply H0 with (t2 :=t1) (x0:=x') (x:=x0) (y:=y') (n:=n) ; auto.
    unfold open. apply* ndr_n_opens.
    apply term_app ; eauto.
    destruct H.
    apply_fresh* term_sub.
    apply H1 with (t2 :=t1) (x0:=x') (x:=y) (y:=y') (n:=n0) ; auto.
    unfold open. apply* ndr_n_opens.
Qed.

Lemma ndr_preservation_term : forall t x y t', term t -> ndr t x y t' -> term t'.
Proof.
  intros t x y t' term_t ndr_. unfolds ndr.
  destruct ndr_ as [n [n_1 [n_fv ndr_]]].
  apply ndr_n_preservation_term with (t:=t) (x:=x) (y:=y) (n:=n) ; auto.
Qed.

Lemma arith_split : forall x y z, x <= y + z -> exists n1, exists n2, n1 <= y /\ n2 <= z /\ x = n1 + n2.
Proof.
  intros x y z H.
  assert (x > y \/ x <= y) as H1 by omega.
  destruct H1.
    exists y. exists (x-y). omega.
    exists x. exists 0. omega.
Qed.

Lemma ndr_n_exists : forall t x y n, n <= fv_occ x t -> exists t', ndr_n t x y t' n.
Proof.
  intros t x y. induction t ; intros ; simpls.
    assert (n0 = 0) by omega. subst. eauto.
    case_var.
      assert(n=0 \/ n=1) as H0 by omega.
      destruct H0 ; subst ; eauto.
      assert(n=0) by omega ; subst. eauto.
    destruct (IHt n) as [t0] ; eauto.
    apply arith_split in H.
    destruct H as [n1] ; destruct H as [n2]. destruct H as [H0]. destruct H as [H1].
    rewrite H. destruct (IHt1 n1) as [t1'] ; auto. destruct (IHt2 n2) as [t2'] ; auto.
    eauto.
    apply arith_split in H.
    destruct H as [n1] ; destruct H as [n2]. destruct H as [H0]. destruct H as [H1].
    rewrite H. destruct (IHt1 n1) as [t1'] ; auto. destruct (IHt2 n2) as [t2'] ; auto.
    eauto.
Qed.

Lemma ndr_exists : forall t x y, 1 < fv_occ x t -> exists t', ndr t x y t'.
Proof.
  intros. unfold ndr.
  destruct (ndr_n_exists t x y 1) as [t'] ; [omega|].
  eauto.
Qed.

Lemma ndr_n_preservation_body : forall t x y t' n, ndr_n t x y t' n -> body t -> body t'.
Proof.
  intros t x y t' n ndr_. gen t' n.
  induction t ; intros.
  inversion ndr_ ; subst*.
  inversion ndr_ ; subst*.
  inversion ndr_ ; subst.
  unfolds body.
  destruct H as [L].
  exists L.
  intros.
  apply ndr_n_preservation_term with (t:=pterm_abs t ^ x0) (x:=x) (y:=y) (n:=n).
    apply H ; auto.
    unfold open. apply ndr_n_opens_gen ; auto.
  inversion ndr_ ; subst*.
  apply body_distribute_over_application.
  apply body_distribute_over_application in H.
  destruct* H.
  inversion ndr_ ; subst.
  unfolds body.
  destruct H as [L].
  exists L.
  intros.
  apply ndr_n_preservation_term with (t:=t1[t2] ^ x0) (x:=x) (y:=y) (n:=n0+n').
    apply* H.
    unfold open. apply* ndr_n_opens_gen.
Qed.

About the interaction between ndr, bv_occ, and fv_occ


Lemma ndr_n_occ_preservation : forall t x y t' n, ndr_n t x y t' n ->
           forall k, ((bv_occ_k k t) = (bv_occ_k k t')).
Proof. intro t. induction t ; intros x y t' m H ; inv H ; try reflexivity ; simpl ; eauto. Qed.

Lemma ndr_n_fv_occ : forall t t' x y n, ~(x = y) -> ndr_n t x y t' n -> fv_occ y t' = fv_occ y t + n.
  intros. induction H0 ; auto ; simpl.
    repeat case_var ; simpl ; auto.
    rewrite IHndr_n1 ; auto. rewrite IHndr_n2 ; auto. omega.
    rewrite IHndr_n1 ; auto. pick_fresh z. rewrite fv_occ_open_var with (y:=z) ; auto.
    rewrite IHndr_n2 ; auto.
    rewrite <-fv_occ_open_var ; auto. omega.
Qed.
Hint Rewrite ndr_n_fv_occ using (solve [auto]) : bv_occ_rewrite.

Lemma ndr_n_gt_fv : forall t t' x y n, ~(x = y) -> ndr_n t x y t' n -> n <= fv_occ x t.
Proof.
  intros t t' x y n x_neq_y ndr_.
  induction ndr_ ; simpl ; eauto.
    case_var*.
    case_var*.
    rewrite H. apply plus_le_compat ; auto.
    rewrite H. apply plus_le_compat ; auto.
Qed.

Lemma ndr_n_fv_occ_x : forall t t' x y n, ~(x = y) -> ndr_n t x y t' n -> fv_occ x t' = fv_occ x t - n.
Proof.
  intros.
  induction H0 ; auto ; simpl.
    repeat case_var ; simpl ; auto.
    case_var*. case_var. auto.
    rewrite IHndr_n1 ; auto. rewrite IHndr_n2 ; auto.
    assert (n <= fv_occ x t). apply ndr_n_gt_fv with (t':=u) (y:=y) ; auto.
    assert (n' <= fv_occ x t'). apply ndr_n_gt_fv with (t':=u') (y:=y) ; auto.
    omega.
    rewrite IHndr_n1 ; auto. pick_fresh z.
    rewrite fv_occ_open_var with (y:=z) ; auto.
    rewrite IHndr_n2 ; auto.
    rewrite <-fv_occ_open_var ; auto.
    assert (n <= fv_occ x t). apply ndr_n_gt_fv with (t':=t') (y:=y) ; auto.
    assert (n' <= fv_occ x u). apply ndr_n_gt_fv with (t':=u') (y:=y) ; auto.
    omega.
Qed.

Lemma ndr_subst : forall t t' u x y n,
        x <>y ->
        y \notin fv t ->
        y \notin fv u ->
        ndr_n t x y t' n ->
        [x~>u]t = [y~>u]([x~>u]t').
Proof.
  intros t t' u x y n x_neq_y y_notin_t y_notin_u ndr_.
  induction ndr_ ; simpl.
  reflexivity.
  case_var. rewrite subst_fresh ; auto.
    simpl. case_var. elim y_notin_t. simpl. VSD.fsetdec.
    reflexivity.
  case_var. case_var. simpl. case_var. reflexivity.
  fequals. apply IHndr_ ; auto.
  simpl in y_notin_t. fequals. apply IHndr_1 ; auto. apply IHndr_2 ; auto.
  simpl in y_notin_t. fequals. apply IHndr_1 ; auto. apply IHndr_2 ; auto.
Qed.

Lemma fv_subst_commute : forall t x y u v, x <>y ->
        x \notin fv v ->
        y \notin fv u ->
        ([y~>v][x~>u]t) = ([x~>u][y~>v]t).
Proof.
intros. induction t ; simpl.
  reflexivity.
  case_var. case_var. simpl. case_var. auto. rewrite subst_fresh ; auto.
  case_var ; simpl. case_var. rewrite subst_fresh ; auto.
  case_var. case_var. auto.
  fequals.
  fequals.
  fequals.
Qed.

Lemma ndr_n_close_open : forall t a b t' u n,
        a \notin fv t -> a <> b ->
        b \notin fv t -> b \notin fv u ->
        a \notin fv u -> ndr_n (t^a) a b t' n ->
        [a~>u]([b~>u]t') = t ^^ u.
Proof.
  intros.
  rewrite fv_subst_commute ; auto.
  rewrite subst_intro with (x:=a) ; auto.
  rewrite <-ndr_subst with (t:=t^a) (n:=n) ; auto.
  apply fv_notin_open ; auto.
Qed.

Tactics using previous lemmas

Ltac get_ndr_root H :=
match (type of H) with
| ndr_n ?t _ _ ?t' _ =>
  match goal with
  | Hroot : ndr_n ?root _ _ t _ |- _ => get_ndr_root Hroot
  | Hroot : ndr ?root _ _ t |- _ => get_ndr_root Hroot
  | _ => H
  end
| ndr ?t _ _ ?t' =>
  match goal with
  | Hroot : ndr_n ?root _ _ t _ |- _ => get_ndr_root Hroot
  | Hroot : ndr ?root _ _ t |- _ => get_ndr_root Hroot
  | _ => H
  end
end.

Ltac ndr_extractor H :=
  match (type of H) with
  | ndr_n ?t _ _ _ _ =>
      let tmp1 := fresh H in
      duplicate H tmp1 ;
      try(apply ndr_n_preservation_term in tmp1 ; [|now auto with term_building_db] ; term_extractor tmp1) ;
      let tmp2 := fresh H in
      duplicate H tmp2 ;
      try (apply ndr_n_preservation_body in tmp2 ; [|now auto with term_building_db] ; term_extractor tmp2) ;
      let tmp3 := fresh H in
      duplicate H tmp3 ;
      try (apply ndr_n_fv_occ in tmp3 ; [|now auto]) ;
      let tmp4 := fresh H in
      duplicate H tmp4 ;
      try (apply ndr_n_fv_occ_x in tmp4 ; first[auto|VSD.fsetdec]) ;
      let tmp5 := fresh H in
      
      remove_if_same H tmp1 ;
      remove_if_same H tmp2 ;
      remove_if_same H tmp3 ;
      remove_if_same H tmp4 ;
      remove_dupterms
  | ndr ?t _ _ _ =>
      let tmp1 := fresh H in
      duplicate H tmp1 ;
      try(apply ndr_preservation_term in tmp1 ; [|now auto with term_building_db] ; term_extractor tmp1) ;

      
      remove_if_same H tmp1 ;

      remove_dupterms
  end.

Ltac saturate_ndr :=
try(
  match goal with
  | H : ndr_n _ _ _ _ _ |- _ =>
    let root := get_ndr_root H in
    ndr_extractor root ; generalize root ; clear root ; saturate_ndr
  | H : ndr _ _ _ _ |- _ =>
    let root := get_ndr_root H in
    ndr_extractor root ; generalize root ; clear root ; saturate_ndr

  | _ => idtac
end).

Ltac term_solver := now (saturate_term ; intros ; saturate_ndr ; intros ; auto with term_building_db ).

Hint Rewrite subst_fv_occ using solve[auto|VSD.fsetdec|term_solver] : bv_occ_rewrite.
Hint Rewrite bv_occ_term using (term_solver) : bv_occ_rewrite.

Ltac saturate_rewrite :=
try(
  match goal with
  | H : ?t = ?t' |- _ => rewrite H ; generalize H ; clear H ; saturate_rewrite
  end
).

Ltac bv_occ_solver := now(
  saturate_term ; intros ; saturate_ndr ; intros ;
  autorewrite with bv_occ_rewrite using (saturate_rewrite ; intros) ;
  simpl ; auto with arith ; try omega).