(** For the examples below, we introduce some sample variable names to
    play with. *)

Definition X : atom := fresh nil.Definition Y : atom := fresh (X :: nil).Definition Z : atom := fresh (X :: Y :: nil).(*************************************************************************)
(** ** Encoding STLC terms *)
(*************************************************************************)


(** We start with examples of encodings in STLC.

  For example, we can encode the expression [(\x. Y x)] as below. We use the
  index [0] because it refers to the closest [abs] to the bound variable
  occurrence. *)

Definition demo_rep1 := abs (app (var_f Y) (var_b 0)).(*                       ^                        |                   *)
(*                       |                        |                   *)
(*                       --------------------------                   *)



(**  Here is another example: the encoding of [(\x. \y. (y x))].      *)

Definition demo_rep2 := abs (abs (app (var_b 0) (var_b 1))).(*                       ^    ^              |         |              *)
(*                       |    |              |         |              *)
(*                       |    ----------------         |              *)
(*                       |                             |              *)
(*                       -------------------------------              *)

(** Finally, here is an example where the same bound variable has two
    different indices, and the same index refers to two different
    bound variables.
                         [\y. ((\x. (x y)) y)]                        *)

Definition demo_rep3 :=
           abs (app (abs (app (var_b 0) (var_b 1))) (var_b 0)).(*          ^         ^              |         |           |          *)
(*          |         |              |         |           |          *)
(*          |         ----------------         |           |          *)
(*          |                                  |           |          *)
(*          ------------------------------------------------          *)


(** *** Exercise: term representation

    Define the following lambda calculus terms using the locally
	 nameless representation.

       "two" : \s. \z. s (s z)

       "COMB_K" : \x. \y. x

       "COMB_S" : \x. \y. \z. x z (y z)

*)

(* SOLUTION *)
Definition two
  := abs (abs (app (var_b 1) (app (var_b 1) (var_b 0)))).Definition COMB_K :=
	abs (abs (var_b 1)).Definition COMB_S :=
   abs (abs (abs
        (app (app (var_b 2) (var_b 0)) (app (var_b 1) (var_b 0))))).

(* Specifications: *)
Lemma demo_open : (app (abs (app (var_b 1) (var_b 0))) (var_b 0)) ^ Y = (app (abs (app (var_f Y) (var_b 0))) (var_f Y)).
Lemma demo_lc : lc_exp (app (abs (app (var_f Y) (var_b 0))) (var_f Y)).
Lemma lc_abs_exists : forall (x : var) e, lc_exp (e ^ x) -> lc_exp (abs e).
Lemma step_lc_exp1 : forall e1 e2, step e1 e2 -> lc_exp e1.
Lemma typing_to_lc_exp : forall E e T, typing E e T -> lc_exp e.
Lemma step_lc_exp2 : forall e1 e2, step e1 e2 -> lc_exp e2.