(*
   Syntax for terms with deBruijn indices
   Venanzio Capretta and Amy Felty       
   March 2006

   see the article
   "Combining de Bruijn Indices and Higher-Order Abstract Syntax in Coq"
*)

Require Export Arith.
Require Export Bool.
Require Export Max.

Inductive empty: Set := .

Lemma Is_true_conj_left:
  forall b1 b2, Is_true (b1 && b2) -> Is_true b1.
Proof.
intro b1; case b1; simpl; trivial.
Defined.

Implicit Arguments Is_true_conj_left [b1 b2].

Lemma Is_true_conj_right:
  forall b1 b2, Is_true (b1 && b2) -> Is_true b2.
Proof.
intros b1 b2; case b1; case b2; simpl; trivial.
Defined.

Implicit Arguments Is_true_conj_right [b1 b2].

Lemma Is_true_conj:
  forall b1 b2, Is_true b1 -> Is_true b2 -> Is_true (b1 && b2).
Proof.
intros b1 b2; case b1; case b2; intro h1; case h1; intro h2; case h2; auto.
Defined.

Implicit Arguments Is_true_conj [b1 b2].

Lemma Is_true_conj_eq:
  forall (b1 b2: bool)(h:Is_true (b1 && b2)),
    h = Is_true_conj (Is_true_conj_left  h) (Is_true_conj_right h).
Proof.
intros b1 b2; case b1; case b2; intro h; case h; auto.
Qed.

Section deBruijn.

Set Implicit Arguments.

Definition var : Set := nat.
Definition var_dec: forall v1 v2:var, {v1=v2}+{v1<>v2}
                  := eq_nat_dec.

Definition bnd : Set := nat.
Definition bnd_dec: forall b1 b2:var, {b1=b2}+{b1<>b2}
                  := eq_nat_dec.

Variable con: Set.

Inductive expr : Set :=
  CON : con -> expr
| VAR : var -> expr
| BND : bnd -> expr
| APP : expr -> expr -> expr
| ABS : expr -> expr.

(* Syntactic recursion on expressions. *)

(* The extension of a family to all subexpressions. *)
Fixpoint fam_star (P: expr->Set)(e:expr){struct e}: Set :=
match e with
  (CON c) => unit
| (VAR v) => unit
| (BND i) => unit
| (APP e1 e2) => (((P e1)*(fam_star P e1))*((P e2)*(fam_star P e2)))%type
| (ABS e') => ((P e')*(fam_star P e'))%type
end.

Fixpoint fam_star_rec
  (P:expr->Set)(H: forall e, fam_star P e -> P e)(e:expr){struct e}:
  fam_star P e :=
match e return (fam_star P e) with
  (CON c) => tt
| (VAR v) => tt
| (BND i) => tt
| (APP e1 e2) => let y1:=(fam_star_rec P H e1) in
                 let y2:=(fam_star_rec P H e2) in
                   ((H e1 y1,y1),(H e2 y2, y2))
| (ABS e') => let y':=(fam_star_rec P H e') in
                (H e' y',y')
end.

Definition expr_star_rec:
  forall (P:expr->Set), (forall e, fam_star P e -> P e) -> forall e, P e :=
fun P H e => H e (fam_star_rec P H e).

(* Syntactic induction on expressions. *)

(* Extension of a predicate to all subexpressions. *)
Inductive pred_star (P: expr->Prop): expr -> Prop:=
  pred_star_CON: forall c, pred_star P (CON c)
| pred_star_VAR: forall v, pred_star P (VAR v)
| pred_star_BND: forall i, pred_star P (BND i)
| pred_star_APP: forall e1 e2, P e1 -> pred_star P e1 ->
                               P e2 -> pred_star P e2 ->
                               pred_star P (APP e1 e2)
| pred_star_ABS: forall e', P e' -> pred_star P e' ->
                            pred_star P (ABS e').

Definition pred_star_induction:
  forall (P:expr->Prop), (forall e, pred_star P e -> P e)
                         -> forall e, pred_star P e.

intros P H e; elim e.
intro c; apply pred_star_CON.
intro v; apply pred_star_VAR.
intro b; apply pred_star_BND.
intros e1 IHe1 e2 IHe2; apply pred_star_APP.
  apply H; apply IHe1.
  apply IHe1.
  apply H; apply IHe2.
  apply IHe2.
intros e' IH; apply pred_star_ABS.
  apply H; apply IH.
  apply IH.
Qed.

Definition expr_star_ind:
  forall (P:expr->Prop), (forall e, pred_star P e -> P e) -> forall e, P e.

intros P H e; apply H.
apply pred_star_induction; exact H.
Qed.

(* Increment by 1 all dangling variables with index >=j. *)
Fixpoint bshift_aux (e:expr)(j:bnd){struct e}: expr :=
match e with
  CON c => CON c
| VAR v => VAR v
| BND i => if (le_lt_dec j i) then (BND (S i)) else (BND i)
| APP e1 e2 => APP (bshift_aux e1 j) (bshift_aux e2 j)
| ABS e' => ABS (bshift_aux e' (S j))
end.

(* Increment by 1 the indeces of all dangling variables. *)
Definition bshift (e:expr): expr := bshift_aux e 0.

(* Bound variable substitution. *)
Fixpoint bsubst (e:expr)(j:bnd)(x:expr){struct e}: expr :=
match e with
  CON c => CON c
| VAR v => VAR v
| BND i => if (bnd_dec i j) then x else (BND i)
| APP e1 e2 => APP (bsubst e1 j x) (bsubst e2 j x)
| ABS e' => ABS (bsubst e' (S j) (bshift x))
end.

(* Swappin of the free variable w and the bound variable j. *)
(* If j doesn't occur, then it just binds w to j! *)
Fixpoint ebind (w:var)(j:bnd)(e:expr){struct e}: expr :=
match e with
  CON c => CON c
| VAR v => if (var_dec v w) then (BND j) else (VAR v)
| BND i => if (bnd_dec i j) then (VAR w) else (BND i)
| APP e1 e2 => APP (ebind w j e1) (ebind w j e2)
| ABS e' => ABS (ebind w (S j) e')
end.

(* A new free variable. *)
Fixpoint newvar (e:expr): var :=
match e with
  CON c => 0
| VAR v => (S v)
| BND i => 0
| APP e1 e2 => max (newvar e1) (newvar e2)
| ABS e' => newvar e'
end.

Lemma bshift_bnd: forall i, bshift (BND i) = BND (S i).
Proof.
auto.
Qed.

Lemma bsubst_bnd:
  forall e i, bsubst e i (BND i) = e.
Proof.
intro e; elim e; auto.
intros j i; simpl.
 case (bnd_dec j i).
 intro Heq; rewrite Heq; reflexivity.
 trivial.
intros e1 IH1 e2 IH2 i; simpl.
  rewrite IH1; rewrite IH2; reflexivity.
intros e' IH i; simpl.
  simpl.
  rewrite bshift_bnd.
  rewrite (IH (S i)).
  reflexivity.
Qed.

Lemma bshift_var: forall v, bshift (VAR v) = VAR v.
Proof.
auto.
Qed.

Lemma ebind_bsubst_aux_eq:
  forall e i n, n>=(newvar e) ->
    ebind n i (bsubst e i (VAR n)) = e.
Proof.
intro e; elim e; auto.
intros v i n Hn; simpl.
 case (var_dec v n).
 simpl in Hn.
 intro Heq; rewrite Heq in Hn.
 red in Hn.
 elim (le_Sn_n n Hn).
 trivial.
intros j i n Hn; simpl.
 case (bnd_dec j i).
 intro H; simpl.
 case (var_dec n n).
 intros _;rewrite H; reflexivity. 
 intro HH; elim HH; trivial.
 intro H; simpl.
 case (bnd_dec j i).
 intro H1; elim H; exact H1.
 trivial.
intros e1 IH1 e2 IH2 i n Hn; simpl.
 rewrite IH1.
 rewrite IH2.
 reflexivity.
 red in Hn; simpl in Hn; red.
 apply le_trans with (2:=Hn). 
 apply le_max_r.
 red in Hn; simpl in Hn; red.
 apply le_trans with (2:=Hn).
 apply le_max_l.
intros e' IH i n Hn; simpl.
 rewrite bshift_var.
 rewrite IH.
 reflexivity.
 exact Hn.
Qed.

(* Lemma 1 from article. *)
Lemma ebind_bsubst_eq:
  forall e i, ebind (newvar e) i (bsubst e i (VAR (newvar e))) = e.
Proof.
intros e i; apply ebind_bsubst_aux_eq.
auto.
Qed.

Unset Implicit Arguments.

End deBruijn.
