(* begin show *)
(** We provide detailed comments for the following proof, mainly to point out several useful tactics and proof techniques.  Starting a proof with "Proof with <some tactic>" allows us to specify a default tactic that should be used to solve goals.  To invoke this default tactic at the end of a proof step, we signal the end of the step with three periods instead of a single one, e.g., "apply typing_weakening...". *)
Proof with simpl_env;
eauto 4 using wf_typ_strengthening,
wf_env_strengthening,
sub_strengthening.
(** The proof proceeds by induction on the given typing derivation for e.  We use the [remember] tactic, along with [generalize dependent], to ensure that the goal is properly strengthened before we use induction. *)
intros U E F x T e u TypT TypU.
remember (F ++ x ~ bind_typ U ++ E) as E'.
generalize dependent F.
induction TypT; intros F EQ; subst; simpl subst_ee...
(** The [typing_var] case involves a case analysis on whether the variable is the same as the one being substituted for. *)
Case "typing_var".
destruct (x0 == x); try subst x0.
(** In the case where [x0=x], we first observe that hypothesis [H0] implies that [T=U], since [x] can only be bound once in the environment.  The conclusion then follows from hypothesis [TypU] and weakening.  We can use the [analyze_binds_uniq] tactic, described in the MetatheoryEnv library, with [H0] to obtain the fact that [T=U]. *)
SCase "x0 = x".
analyze_binds_uniq H0.
injection BindsTacVal; intros; subst.
(** In order to apply [typing_weakening], we need to rewrite the environment so that it has the right shape.  (We could also prove a corollary of typing_weakening.)  The [rewrite_env] tactic, described in the Environment library, is one way to perform this rewriting. *)
rewrite_env (empty ++ F ++ E).
apply typing_weakening...
(** In the case where [x0<>x], the result follows by an exhaustive case analysis on exactly where [x0] is bound in the environment.  We perform this case analysis by using the [analyze_binds] tactic, described in the MetatheoryEnv library. *)
SCase "x0 <> x".
analyze_binds H0.
eauto using wf_env_strengthening.
eauto using wf_env_strengthening.
(** Informally, the [typing_abs] case is a straightforward application of the induction hypothesis, which is called [H0] here. *)
Case "typing_abs".
(** We use the "pick fresh and apply" tactic to apply the rule [typing_abs] without having to calculate the appropriate finite set of atoms. *)
pick fresh y and apply typing_abs.
(** We cannot apply [H0] directly here.  The first problem is that the induction hypothesis has [(subst_ee open_ee)], whereas in the goal we have [(open_ee subst_ee)].  The lemma [subst_ee_open_ee_var] lets us swap the order of these two operations. *)
rewrite subst_ee_open_ee_var...
(** The second problem is how the concatenations are associated in the environments.  In the goal, we currently have  <<       (y ~ bind_typ V ++ F ++ E), >> where concatenation associates to the right.  In order to apply the induction hypothesis, we need  <<        ((y ~ bind_typ V ++ F) ++ E). >> We can use the [rewrite_env] tactic to perform this rewriting, or we can rewrite directly with an appropriate lemma from the MetatheoryEnv library. *)
rewrite <- app_assoc.
(** Now we can apply the induction hypothesis. *)
apply H0...
(** The remaining cases in this proof are straightforward, given everything that we have pointed out above. *)
Case "typing_tabs".
pick fresh Y and apply typing_tabs.
rewrite subst_ee_open_te_var...
rewrite <- app_assoc.
apply H0...
Case "typing_let".
pick fresh y and apply typing_let...
rewrite subst_ee_open_ee_var...
rewrite <- app_assoc.
apply H0...
Case "typing_case".
pick fresh y and apply typing_case...
rewrite subst_ee_open_ee_var...
rewrite <- app_assoc.
apply H0...
rewrite subst_ee_open_ee_var...
rewrite <- app_assoc.
apply H2...
Qed.
(* end show *)
(************************************************************************ *)
(** ** Type substitution preserves typing (11) *)

Lemma typing_through_subst_te : forall Q E F Z e T P,
typing (F ++ Z ~ bind_sub Q ++ E) e T ->
sub E P Q ->
typing (map (subst_tb Z P) F ++ E) (subst_te Z P e) (subst_tt Z P T).