(***********************************************************************)
(**  * Connecting nominal and LN semantics *)
(***********************************************************************)
(** Our final goal is to show that the abstract nominal machine implements the same semantics as the LN substitution-based small step relation.  We'll do this by proving that any time the abstract machine takes a step, we can decode the machine configuration before and after the step to LN expressions, and those expressions are either identical or related by the LN step relation.  << machine_step (h,t,s)  ------>  (h',t',s')            nominal terms |                  | | decode           | decode v                  v e       - - ->     e'           locally nameless terms step e e' or e = e' >>  This result is the [simulate_step] lemma near the end of this file.  *)
(***********************************************************************)
(** ** Translating nominal terms to LN terms *)
(***********************************************************************)
(** We decode named terms to LN terms through the use of the [close_exp_wrt_exp] function. This function replaces all occurrences of a given atom by a new bound variable.  This function is defined in the [Stlc.Lemmas] module and can be automatically generated by LNgen.  *)
Fixpoint nom_to_exp (ne : n_exp) : exp :=
match ne with
| n_var x => var_f x
| n_app e1 e2 => app (nom_to_exp e1) (nom_to_exp e2)
| n_abs x e1 => abs (close_exp_wrt_exp x (nom_to_exp e1))
end.
(** We also define a translation from machine configurations to LN terms. In this case we must substitute all definitions in the heap through the terms and create all of the applications in the stack. *)
Fixpoint apply_heap (h : heap) (e : exp) : exp  :=
match h with
| nil => e
| (x , e') :: h' => apply_heap h' ([x ~> nom_to_exp e'] e)
end.
(* Note that the stack could have some heap definitions in it, so we apply the substitutions in the heap there too. *)
Fixpoint apply_stack h (s : list frame) (e :exp) : exp :=
match s with
| nil => e
| n_app2 e' :: s' => apply_stack h s' (app e (apply_heap h (nom_to_exp e')))
end.
(** The full decode function puts all of these parts together. *)
Definition decode (c:configuration) : exp  :=
match c with
| (h,e,s) => apply_stack h s (apply_heap h (nom_to_exp e))
end.
(** Here's an example translation from the machine step demo. *)
Definition conf1 := ([(Y,n_var Z)], n_abs X (n_var X), [n_app2 (n_var Y)]).
Example decode1 : decode conf1 = (app (abs (var_b 0)) (var_f Z)).
(***********************************************************************)
(** ** Connecting free variable functions.                             *)
(***********************************************************************)
(** Here is the first result about our decoding: that the two free variable functions agree.  In this part of the file, we will take advantage of automation provided by the Lemmas module. In particular, this module defines a database of rewriting hints (called [lngen]) that can be used to automatically rewrite LN terms into simpler form. *)
(** As we prove new lemmas, we can also extend the hint database with new rewritings. *)
Hint Rewrite fv_nom_fv_exp_eq : lngen.
Hint Resolve fv_nom_fv_exp_eq : lngen.
(** The Metatheory library contains two powerful tactics for simplifying goals:  - [default_steps]: repeat a bunch of simplifying steps, such as simplifying the goal, inverting simple hypotheses, etc.  - [default_simp]: above plus case analysis for booleans and other sums  Below, we modify the behavior of these tactics by updating the following two definitions, so that the [lngen] hint databases will be available.  *)
Ltac default_auto        ::= auto with lngen.
Ltac default_autorewrite ::= autorewrite with lngen.
(** We also add a few more rewriting lemmas to the hint database to automate our proofs. *)
Hint Rewrite subst_exp_open_exp_wrt_exp : lngen.
Hint Rewrite swap_size_eq : lngen.
Hint Resolve le_S_n : lngen.
(***********************************************************************)
(** ** Decoded terms are locally closed *)
(***********************************************************************)
(** Next, we show that our decoding of nominal terms and configurations produces locally closed LN terms.  Again, these proofs are highly automatable. *)
Hint Resolve nom_to_exp_lc : lngen.
Hint Resolve apply_heap_lc : lngen.
(** *** Exercise: [apply_stack_lc]  State and prove a lemma called [apply_stack_lc] and add it to the [lngen] hint database so that the proof for [decode_lc] goes through. *)

(* SOLUTION *)
Lemma apply_stack_lc : forall s h e,
lc_exp e -> lc_exp (apply_stack h s e).